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

Merged revisions 4186 to 4454 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/sqlalchemy@4455 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robin Munn 2007-01-31 23:43:09 +00:00
parent 122426e745
commit fe361e678a
120 changed files with 5900 additions and 1899 deletions

View File

@ -78,6 +78,7 @@ answer newbie questions, and generally made Django that much better:
Clint Ecker Clint Ecker
Enrico <rico.bl@gmail.com> Enrico <rico.bl@gmail.com>
favo@exoweb.net favo@exoweb.net
Eric Floehr <eric@intellovations.com>
gandalf@owca.info gandalf@owca.info
Baishampayan Ghose Baishampayan Ghose
martin.glueck@gmail.com martin.glueck@gmail.com
@ -118,13 +119,16 @@ answer newbie questions, and generally made Django that much better:
Manuzhai Manuzhai
Petar Marić Petar Marić
mark@junklight.com mark@junklight.com
Yasushi Masuda <whosaysni@gmail.com>
mattycakes@gmail.com mattycakes@gmail.com
Jason McBrayer <http://www.carcosa.net/jason/> Jason McBrayer <http://www.carcosa.net/jason/>
mccutchen@gmail.com mccutchen@gmail.com
michael.mcewan@gmail.com michael.mcewan@gmail.com
mitakummaa@gmail.com
mmarshall mmarshall
Eric Moritz <http://eric.themoritzfamily.com/> Eric Moritz <http://eric.themoritzfamily.com/>
Robin Munn <http://www.geekforgod.com/> Robin Munn <http://www.geekforgod.com/>
Robert Myers <myer0052@gmail.com>
Nebojša Dorđević Nebojša Dorđević
Fraser Nevett <mail@nevett.org> Fraser Nevett <mail@nevett.org>
Sam Newman <http://www.magpiebrain.com/> Sam Newman <http://www.magpiebrain.com/>
@ -149,6 +153,7 @@ answer newbie questions, and generally made Django that much better:
serbaut@gmail.com serbaut@gmail.com
Pete Shinners <pete@shinners.org> Pete Shinners <pete@shinners.org>
SmileyChris <smileychris@gmail.com> SmileyChris <smileychris@gmail.com>
smurf@smurf.noris.de
sopel sopel
Thomas Steinacher <tom@eggdrop.ch> Thomas Steinacher <tom@eggdrop.ch>
nowell strite nowell strite
@ -160,6 +165,7 @@ answer newbie questions, and generally made Django that much better:
Tom Insam Tom Insam
Joe Topjian <http://joe.terrarum.net/geek/code/python/django/> Joe Topjian <http://joe.terrarum.net/geek/code/python/django/>
Karen Tracey <graybark@bellsouth.net> Karen Tracey <graybark@bellsouth.net>
Makoto Tsuyuki <mtsuyuki@gmail.com>
Amit Upadhyay Amit Upadhyay
Geert Vanderkelen Geert Vanderkelen
Milton Waddams Milton Waddams

View File

@ -1,3 +1,5 @@
#!/usr/bin/env python
""" """
Daily cleanup job. Daily cleanup job.

View File

@ -25,7 +25,7 @@ ADMINS = ()
INTERNAL_IPS = () INTERNAL_IPS = ()
# Local time zone for this installation. All choices can be found here: # Local time zone for this installation. All choices can be found here:
# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE # http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
TIME_ZONE = 'America/Chicago' TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here: # Language code for this installation. All choices can be found here:

View File

@ -17,7 +17,7 @@ DATABASE_HOST = '' # Set to empty string for localhost. Not used wit
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
# Local time zone for this installation. All choices can be found here: # Local time zone for this installation. All choices can be found here:
# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE # http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
TIME_ZONE = 'America/Chicago' TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here: # Language code for this installation. All choices can be found here:

View File

@ -0,0 +1,52 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_modify adminmedia %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="../../../../jsi18n/"></script>
{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %}
{% endblock %}
{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block breadcrumbs %}{% if not is_popup %}
<div class="breadcrumbs">
<a href="../../../../">{% trans "Home" %}</a> &rsaquo;
<a href="../../">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo;
<a href="../">{{ original|truncatewords:"18"|escape }}</a> &rsaquo;
{% trans 'Change password' %}
</div>
{% endif %}{% endblock %}
{% block content %}<div id="content-main">
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
<div>
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
{% if form.error_dict %}
<p class="errornote">
{% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
</p>
{% endif %}
<p>{% blocktrans with original.username|escape as username %}Enter a new password for the user <strong>{{ username }}</strong>.{% endblocktrans %}</p>
<fieldset class="module aligned">
<div class="form-row">
{{ form.password1.html_error_list }}
<label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }}
</div>
<div class="form-row">
{{ form.password2.html_error_list }}
<label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }}
<p class="help">{% trans 'Enter the same password as above, for verification.' %}</p>
</div>
</fieldset>
<div class="submit-row">
<input type="submit" value="{% trans 'Change password' %}" class="default" />
</div>
<script type="text/javascript">document.getElementById("{{ first_form_field_id }}").focus();</script>
</div>
</form></div>
{% endblock %}

View File

@ -38,7 +38,10 @@
<div id="content" class="{% block coltype %}colM{% endblock %}"> <div id="content" class="{% block coltype %}colM{% endblock %}">
{% block pretitle %}{% endblock %} {% block pretitle %}{% endblock %}
{% block content_title %}{% if title %}<h1>{{ title|escape }}</h1>{% endif %}{% endblock %} {% block content_title %}{% if title %}<h1>{{ title|escape }}</h1>{% endif %}{% endblock %}
{% block content %}{{ content }}{% endblock %} {% block content %}
{% block object-tools %}{% endblock %}
{{ content }}
{% endblock %}
{% block sidebar %}{% endblock %} {% block sidebar %}{% endblock %}
<br class="clear" /> <br class="clear" />
</div> </div>

View File

@ -16,11 +16,13 @@
</div> </div>
{% endif %}{% endblock %} {% endif %}{% endblock %}
{% block content %}<div id="content-main"> {% block content %}<div id="content-main">
{% block object-tools %}
{% if change %}{% if not is_popup %} {% if change %}{% if not is_popup %}
<ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li> <ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%} {% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
</ul> </ul>
{% endif %}{% endif %} {% endif %}{% endif %}
{% endblock %}
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %} <form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
<div> <div>
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %} {% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}

View File

@ -7,9 +7,11 @@
{% block coltype %}flex{% endblock %} {% block coltype %}flex{% endblock %}
{% block content %} {% block content %}
<div id="content-main"> <div id="content-main">
{% block object-tools %}
{% if has_add_permission %} {% if has_add_permission %}
<ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{ name }}{% endblocktrans %}</a></li></ul> <ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
{% endif %} {% endif %}
{% endblock %}
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist"> <div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
{% block search %}{% search_form cl %}{% endblock %} {% block search %}{% search_form cl %}{% endblock %}
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %} {% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}

View File

@ -101,6 +101,10 @@ def result_headers(cl):
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')} "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
def _boolean_icon(field_val):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
return '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
def items_for_result(cl, result): def items_for_result(cl, result):
first = True first = True
pk = cl.lookup_opts.pk.attname pk = cl.lookup_opts.pk.attname
@ -114,9 +118,14 @@ def items_for_result(cl, result):
try: try:
attr = getattr(result, field_name) attr = getattr(result, field_name)
allow_tags = getattr(attr, 'allow_tags', False) allow_tags = getattr(attr, 'allow_tags', False)
boolean = getattr(attr, 'boolean', False)
if callable(attr): if callable(attr):
attr = attr() attr = attr()
result_repr = str(attr) if boolean:
allow_tags = True
result_repr = _boolean_icon(attr)
else:
result_repr = str(attr)
except (AttributeError, ObjectDoesNotExist): except (AttributeError, ObjectDoesNotExist):
result_repr = EMPTY_CHANGELIST_VALUE result_repr = EMPTY_CHANGELIST_VALUE
else: else:
@ -147,8 +156,7 @@ def items_for_result(cl, result):
row_class = ' class="nowrap"' row_class = ' class="nowrap"'
# Booleans are special: We use images. # Booleans are special: We use images.
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField): elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} result_repr = _boolean_icon(field_val)
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
# FloatFields are special: Zero-pad the decimals. # FloatFields are special: Zero-pad the decimals.
elif isinstance(f, models.FloatField): elif isinstance(f, models.FloatField):
if field_val is not None: if field_val is not None:

View File

@ -29,6 +29,8 @@ urlpatterns = patterns('',
# "Add user" -- a special-case view # "Add user" -- a special-case view
('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'), ('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
# "Change user password" -- another special-case view
('^auth/user/(\d+)/password/$', 'django.contrib.admin.views.auth.user_change_password'),
# Add/change/delete/history # Add/change/delete/history
('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'), ('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'),

View File

@ -1,10 +1,11 @@
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm, AdminPasswordChangeForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django import forms, template from django import oldforms, template
from django.shortcuts import render_to_response from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.html import escape
def user_add_stage(request): def user_add_stage(request):
if not request.user.has_perm('auth.change_user'): if not request.user.has_perm('auth.change_user'):
@ -24,7 +25,7 @@ def user_add_stage(request):
return HttpResponseRedirect('../%s/' % new_user.id) return HttpResponseRedirect('../%s/' % new_user.id)
else: else:
errors = new_data = {} errors = new_data = {}
form = forms.FormWrapper(manipulator, new_data, errors) form = oldforms.FormWrapper(manipulator, new_data, errors)
return render_to_response('admin/auth/user/add_form.html', { return render_to_response('admin/auth/user/add_form.html', {
'title': _('Add user'), 'title': _('Add user'),
'form': form, 'form': form,
@ -42,3 +43,35 @@ def user_add_stage(request):
'username_help_text': User._meta.get_field('username').help_text, 'username_help_text': User._meta.get_field('username').help_text,
}, context_instance=template.RequestContext(request)) }, context_instance=template.RequestContext(request))
user_add_stage = staff_member_required(user_add_stage) user_add_stage = staff_member_required(user_add_stage)
def user_change_password(request, id):
if not request.user.has_perm('auth.change_user'):
raise PermissionDenied
user = get_object_or_404(User, pk=id)
manipulator = AdminPasswordChangeForm(user)
if request.method == 'POST':
new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data)
if not errors:
new_user = manipulator.save(new_data)
msg = _('Password changed successfully.')
request.user.message_set.create(message=msg)
return HttpResponseRedirect('..')
else:
errors = new_data = {}
form = oldforms.FormWrapper(manipulator, new_data, errors)
return render_to_response('admin/auth/user/change_password.html', {
'title': _('Change password: %s') % escape(user.username),
'form': form,
'is_popup': request.REQUEST.has_key('_popup'),
'add': True,
'change': False,
'has_delete_permission': False,
'has_change_permission': True,
'has_absolute_url': False,
'first_form_field_id': 'id_password1',
'opts': User._meta,
'original': user,
'show_save': True,
}, context_instance=template.RequestContext(request))
user_change_password = staff_member_required(user_change_password)

View File

@ -1,4 +1,4 @@
from django import forms, template from django import oldforms, template
from django.conf import settings from django.conf import settings
from django.contrib.admin.filterspecs import FilterSpec from django.contrib.admin.filterspecs import FilterSpec
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
@ -46,8 +46,8 @@ def quote(s):
""" """
Ensure that primary key values do not confuse the admin URLs by escaping Ensure that primary key values do not confuse the admin URLs by escaping
any '/', '_' and ':' characters. Similar to urllib.quote, except that the any '/', '_' and ':' characters. Similar to urllib.quote, except that the
quoting is slightly different so that it doesn't get autoamtically quoting is slightly different so that it doesn't get automatically
unquoted by the web browser. unquoted by the Web browser.
""" """
if type(s) != type(''): if type(s) != type(''):
return s return s
@ -283,7 +283,7 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po
errors = {} errors = {}
# Populate the FormWrapper. # Populate the FormWrapper.
form = forms.FormWrapper(manipulator, new_data, errors) form = oldforms.FormWrapper(manipulator, new_data, errors)
c = template.RequestContext(request, { c = template.RequestContext(request, {
'title': _('Add %s') % opts.verbose_name, 'title': _('Add %s') % opts.verbose_name,
@ -374,7 +374,7 @@ def change_stage(request, app_label, model_name, object_id):
errors = {} errors = {}
# Populate the FormWrapper. # Populate the FormWrapper.
form = forms.FormWrapper(manipulator, new_data, errors) form = oldforms.FormWrapper(manipulator, new_data, errors)
form.original = manipulator.original_object form.original = manipulator.original_object
form.order_objects = [] form.order_objects = []

View File

@ -1,6 +1,6 @@
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.core import validators from django.core import validators
from django import template, forms from django import template, oldforms
from django.template import loader from django.template import loader
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
@ -25,17 +25,17 @@ def template_validator(request):
request.user.message_set.create(message='The template is valid.') request.user.message_set.create(message='The template is valid.')
return render_to_response('admin/template_validator.html', { return render_to_response('admin/template_validator.html', {
'title': 'Template validator', 'title': 'Template validator',
'form': forms.FormWrapper(manipulator, new_data, errors), 'form': oldforms.FormWrapper(manipulator, new_data, errors),
}, context_instance=template.RequestContext(request)) }, context_instance=template.RequestContext(request))
template_validator = staff_member_required(template_validator) template_validator = staff_member_required(template_validator)
class TemplateValidator(forms.Manipulator): class TemplateValidator(oldforms.Manipulator):
def __init__(self, settings_modules): def __init__(self, settings_modules):
self.settings_modules = settings_modules self.settings_modules = settings_modules
site_list = Site.objects.in_bulk(settings_modules.keys()).values() site_list = Site.objects.in_bulk(settings_modules.keys()).values()
self.fields = ( self.fields = (
forms.SelectField('site', is_required=True, choices=[(s.id, s.name) for s in site_list]), oldforms.SelectField('site', is_required=True, choices=[(s.id, s.name) for s in site_list]),
forms.LargeTextField('template', is_required=True, rows=25, validator_list=[self.isValidTemplate]), oldforms.LargeTextField('template', is_required=True, rows=25, validator_list=[self.isValidTemplate]),
) )
def isValidTemplate(self, field_data, all_data): def isValidTemplate(self, field_data, all_data):

View File

@ -3,16 +3,16 @@ from django.contrib.auth import authenticate
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.template import Context, loader from django.template import Context, loader
from django.core import validators from django.core import validators
from django import forms from django import oldforms
class UserCreationForm(forms.Manipulator): class UserCreationForm(oldforms.Manipulator):
"A form that creates a user, with no privileges, from the given username and password." "A form that creates a user, with no privileges, from the given username and password."
def __init__(self): def __init__(self):
self.fields = ( self.fields = (
forms.TextField(field_name='username', length=30, maxlength=30, is_required=True, oldforms.TextField(field_name='username', length=30, maxlength=30, is_required=True,
validator_list=[validators.isAlphaNumeric, self.isValidUsername]), validator_list=[validators.isAlphaNumeric, self.isValidUsername]),
forms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True), oldforms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
forms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True, oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]), validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
) )
@ -27,7 +27,7 @@ class UserCreationForm(forms.Manipulator):
"Creates the user." "Creates the user."
return User.objects.create_user(new_data['username'], '', new_data['password1']) return User.objects.create_user(new_data['username'], '', new_data['password1'])
class AuthenticationForm(forms.Manipulator): class AuthenticationForm(oldforms.Manipulator):
""" """
Base class for authenticating users. Extend this to get a form that accepts Base class for authenticating users. Extend this to get a form that accepts
username/password logins. username/password logins.
@ -41,9 +41,9 @@ class AuthenticationForm(forms.Manipulator):
""" """
self.request = request self.request = request
self.fields = [ self.fields = [
forms.TextField(field_name="username", length=15, maxlength=30, is_required=True, oldforms.TextField(field_name="username", length=15, maxlength=30, is_required=True,
validator_list=[self.isValidUser, self.hasCookiesEnabled]), validator_list=[self.isValidUser, self.hasCookiesEnabled]),
forms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True), oldforms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True),
] ]
self.user_cache = None self.user_cache = None
@ -68,11 +68,11 @@ class AuthenticationForm(forms.Manipulator):
def get_user(self): def get_user(self):
return self.user_cache return self.user_cache
class PasswordResetForm(forms.Manipulator): class PasswordResetForm(oldforms.Manipulator):
"A form that lets a user request a password reset" "A form that lets a user request a password reset"
def __init__(self): def __init__(self):
self.fields = ( self.fields = (
forms.EmailField(field_name="email", length=40, is_required=True, oldforms.EmailField(field_name="email", length=40, is_required=True,
validator_list=[self.isValidUserEmail]), validator_list=[self.isValidUserEmail]),
) )
@ -105,16 +105,16 @@ class PasswordResetForm(forms.Manipulator):
} }
send_mail('Password reset on %s' % site_name, t.render(Context(c)), None, [self.user_cache.email]) send_mail('Password reset on %s' % site_name, t.render(Context(c)), None, [self.user_cache.email])
class PasswordChangeForm(forms.Manipulator): class PasswordChangeForm(oldforms.Manipulator):
"A form that lets a user change his password." "A form that lets a user change his password."
def __init__(self, user): def __init__(self, user):
self.user = user self.user = user
self.fields = ( self.fields = (
forms.PasswordField(field_name="old_password", length=30, maxlength=30, is_required=True, oldforms.PasswordField(field_name="old_password", length=30, maxlength=30, is_required=True,
validator_list=[self.isValidOldPassword]), validator_list=[self.isValidOldPassword]),
forms.PasswordField(field_name="new_password1", length=30, maxlength=30, is_required=True, oldforms.PasswordField(field_name="new_password1", length=30, maxlength=30, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]), validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]),
forms.PasswordField(field_name="new_password2", length=30, maxlength=30, is_required=True), oldforms.PasswordField(field_name="new_password2", length=30, maxlength=30, is_required=True),
) )
def isValidOldPassword(self, new_data, all_data): def isValidOldPassword(self, new_data, all_data):
@ -126,3 +126,18 @@ class PasswordChangeForm(forms.Manipulator):
"Saves the new password." "Saves the new password."
self.user.set_password(new_data['new_password1']) self.user.set_password(new_data['new_password1'])
self.user.save() self.user.save()
class AdminPasswordChangeForm(oldforms.Manipulator):
"A form used to change the password of a user in the admin interface."
def __init__(self, user):
self.user = user
self.fields = (
oldforms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
)
def save(self, new_data):
"Saves the new password."
self.user.set_password(new_data['password1'])
self.user.save()

View File

@ -91,7 +91,7 @@ class User(models.Model):
first_name = models.CharField(_('first name'), maxlength=30, blank=True) first_name = models.CharField(_('first name'), maxlength=30, blank=True)
last_name = models.CharField(_('last name'), maxlength=30, blank=True) last_name = models.CharField(_('last name'), maxlength=30, blank=True)
email = models.EmailField(_('e-mail address'), blank=True) email = models.EmailField(_('e-mail address'), blank=True)
password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]'")) password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site.")) is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user can log into the Django admin. Unselect this instead of deleting accounts.")) is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user can log into the Django admin. Unselect this instead of deleting accounts."))
is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them.")) is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))

View File

@ -1,6 +1,6 @@
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm
from django import forms from django import oldforms
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.template import RequestContext from django.template import RequestContext
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
@ -26,7 +26,7 @@ def login(request, template_name='registration/login.html'):
errors = {} errors = {}
request.session.set_test_cookie() request.session.set_test_cookie()
return render_to_response(template_name, { return render_to_response(template_name, {
'form': forms.FormWrapper(manipulator, request.POST, errors), 'form': oldforms.FormWrapper(manipulator, request.POST, errors),
REDIRECT_FIELD_NAME: redirect_to, REDIRECT_FIELD_NAME: redirect_to,
'site_name': Site.objects.get_current().name, 'site_name': Site.objects.get_current().name,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@ -62,7 +62,7 @@ def password_reset(request, is_admin_site=False, template_name='registration/pas
else: else:
form.save(email_template_name=email_template_name) 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': oldforms.FormWrapper(form, new_data, errors)},
context_instance=RequestContext(request)) context_instance=RequestContext(request))
def password_reset_done(request, template_name='registration/password_reset_done.html'): def password_reset_done(request, template_name='registration/password_reset_done.html'):
@ -77,7 +77,7 @@ def password_change(request, template_name='registration/password_change_form.ht
if not errors: if not errors:
form.save(new_data) form.save(new_data)
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': oldforms.FormWrapper(form, new_data, errors)},
context_instance=RequestContext(request)) context_instance=RequestContext(request))
password_change = login_required(password_change) password_change = login_required(password_change)

View File

@ -1,5 +1,5 @@
from django.core import validators from django.core import validators
from django import forms from django import oldforms
from django.core.mail import mail_admins, mail_managers from django.core.mail import mail_admins, mail_managers
from django.http import Http404 from django.http import Http404
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
@ -28,37 +28,37 @@ class PublicCommentManipulator(AuthenticationForm):
else: else:
return [] return []
self.fields.extend([ self.fields.extend([
forms.LargeTextField(field_name="comment", maxlength=3000, is_required=True, oldforms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
validator_list=[self.hasNoProfanities]), validator_list=[self.hasNoProfanities]),
forms.RadioSelectField(field_name="rating1", choices=choices, oldforms.RadioSelectField(field_name="rating1", choices=choices,
is_required=ratings_required and num_rating_choices > 0, is_required=ratings_required and num_rating_choices > 0,
validator_list=get_validator_list(1), validator_list=get_validator_list(1),
), ),
forms.RadioSelectField(field_name="rating2", choices=choices, oldforms.RadioSelectField(field_name="rating2", choices=choices,
is_required=ratings_required and num_rating_choices > 1, is_required=ratings_required and num_rating_choices > 1,
validator_list=get_validator_list(2), validator_list=get_validator_list(2),
), ),
forms.RadioSelectField(field_name="rating3", choices=choices, oldforms.RadioSelectField(field_name="rating3", choices=choices,
is_required=ratings_required and num_rating_choices > 2, is_required=ratings_required and num_rating_choices > 2,
validator_list=get_validator_list(3), validator_list=get_validator_list(3),
), ),
forms.RadioSelectField(field_name="rating4", choices=choices, oldforms.RadioSelectField(field_name="rating4", choices=choices,
is_required=ratings_required and num_rating_choices > 3, is_required=ratings_required and num_rating_choices > 3,
validator_list=get_validator_list(4), validator_list=get_validator_list(4),
), ),
forms.RadioSelectField(field_name="rating5", choices=choices, oldforms.RadioSelectField(field_name="rating5", choices=choices,
is_required=ratings_required and num_rating_choices > 4, is_required=ratings_required and num_rating_choices > 4,
validator_list=get_validator_list(5), validator_list=get_validator_list(5),
), ),
forms.RadioSelectField(field_name="rating6", choices=choices, oldforms.RadioSelectField(field_name="rating6", choices=choices,
is_required=ratings_required and num_rating_choices > 5, is_required=ratings_required and num_rating_choices > 5,
validator_list=get_validator_list(6), validator_list=get_validator_list(6),
), ),
forms.RadioSelectField(field_name="rating7", choices=choices, oldforms.RadioSelectField(field_name="rating7", choices=choices,
is_required=ratings_required and num_rating_choices > 6, is_required=ratings_required and num_rating_choices > 6,
validator_list=get_validator_list(7), validator_list=get_validator_list(7),
), ),
forms.RadioSelectField(field_name="rating8", choices=choices, oldforms.RadioSelectField(field_name="rating8", choices=choices,
is_required=ratings_required and num_rating_choices > 7, is_required=ratings_required and num_rating_choices > 7,
validator_list=get_validator_list(8), validator_list=get_validator_list(8),
), ),
@ -117,13 +117,13 @@ class PublicCommentManipulator(AuthenticationForm):
mail_managers("Comment posted by sketchy user (%s)" % self.user_cache.username, c.get_as_text()) mail_managers("Comment posted by sketchy user (%s)" % self.user_cache.username, c.get_as_text())
return c return c
class PublicFreeCommentManipulator(forms.Manipulator): class PublicFreeCommentManipulator(oldforms.Manipulator):
"Manipulator that handles public free (unregistered) comments" "Manipulator that handles public free (unregistered) comments"
def __init__(self): def __init__(self):
self.fields = ( self.fields = (
forms.TextField(field_name="person_name", maxlength=50, is_required=True, oldforms.TextField(field_name="person_name", maxlength=50, is_required=True,
validator_list=[self.hasNoProfanities]), validator_list=[self.hasNoProfanities]),
forms.LargeTextField(field_name="comment", maxlength=3000, is_required=True, oldforms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
validator_list=[self.hasNoProfanities]), validator_list=[self.hasNoProfanities]),
) )
@ -221,9 +221,9 @@ def post_comment(request):
from django.contrib.auth import login from django.contrib.auth import login
login(request, manipulator.get_user()) login(request, manipulator.get_user())
if errors or request.POST.has_key('preview'): if errors or request.POST.has_key('preview'):
class CommentFormWrapper(forms.FormWrapper): class CommentFormWrapper(oldforms.FormWrapper):
def __init__(self, manipulator, new_data, errors, rating_choices): def __init__(self, manipulator, new_data, errors, rating_choices):
forms.FormWrapper.__init__(self, manipulator, new_data, errors) oldforms.FormWrapper.__init__(self, manipulator, new_data, errors)
self.rating_choices = rating_choices self.rating_choices = rating_choices
def ratings(self): def ratings(self):
field_list = [self['rating%d' % (i+1)] for i in range(len(rating_choices))] field_list = [self['rating%d' % (i+1)] for i in range(len(rating_choices))]
@ -302,7 +302,7 @@ def post_free_comment(request):
comment = errors and '' or manipulator.get_comment(new_data) comment = errors and '' or manipulator.get_comment(new_data)
return render_to_response('comments/free_preview.html', { return render_to_response('comments/free_preview.html', {
'comment': comment, 'comment': comment,
'comment_form': forms.FormWrapper(manipulator, new_data, errors), 'comment_form': oldforms.FormWrapper(manipulator, new_data, errors),
'options': options, 'options': options,
'target': target, 'target': target,
'hash': security_hash, 'hash': security_hash,

View File

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
CONTENT_TYPE_CACHE = {}
class ContentTypeManager(models.Manager): class ContentTypeManager(models.Manager):
def get_for_model(self, model): def get_for_model(self, model):
""" """
@ -8,10 +9,15 @@ class ContentTypeManager(models.Manager):
ContentType if necessary. ContentType if necessary.
""" """
opts = model._meta opts = model._meta
# The str() is needed around opts.verbose_name because it's a key = (opts.app_label, opts.object_name.lower())
# django.utils.functional.__proxy__ object. try:
ct, created = self.model._default_manager.get_or_create(app_label=opts.app_label, ct = CONTENT_TYPE_CACHE[key]
model=opts.object_name.lower(), defaults={'name': str(opts.verbose_name)}) except KeyError:
# The str() is needed around opts.verbose_name because it's a
# django.utils.functional.__proxy__ object.
ct, created = self.model._default_manager.get_or_create(app_label=key[0],
model=key[1], defaults={'name': str(opts.verbose_name)})
CONTENT_TYPE_CACHE[key] = ct
return ct return ct
class ContentType(models.Model): class ContentType(models.Model):

View File

@ -11,7 +11,7 @@ import md5
import re import re
import itertools import itertools
_ERROR_MSG = "<h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p>" _ERROR_MSG = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>'
_POST_FORM_RE = \ _POST_FORM_RE = \
re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE) re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)

View File

@ -48,6 +48,7 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.http import Http404 from django.http import Http404
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.template.context import RequestContext
import cPickle as pickle import cPickle as pickle
import md5 import md5
@ -91,7 +92,9 @@ class FormPreview(object):
def preview_get(self, request): def preview_get(self, request):
"Displays the form" "Displays the form"
f = self.form(auto_id=AUTO_ID) f = self.form(auto_id=AUTO_ID)
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state}) return render_to_response(self.form_template,
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
context_instance=RequestContext(request))
def preview_post(self, request): def preview_post(self, request):
"Validates the POST data. If valid, displays the preview page. Else, redisplays form." "Validates the POST data. If valid, displays the preview page. Else, redisplays form."
@ -100,9 +103,9 @@ class FormPreview(object):
if f.is_valid(): if f.is_valid():
context['hash_field'] = self.unused_name('hash') context['hash_field'] = self.unused_name('hash')
context['hash_value'] = self.security_hash(request, f) context['hash_value'] = self.security_hash(request, f)
return render_to_response(self.preview_template, context) return render_to_response(self.preview_template, context, context_instance=RequestContext(request))
else: else:
return render_to_response(self.form_template, context) return render_to_response(self.form_template, context, context_instance=RequestContext(request))
def post_post(self, request): def post_post(self, request):
"Validates the POST data. If valid, calls done(). Else, redisplays form." "Validates the POST data. If valid, calls done(). Else, redisplays form."
@ -112,7 +115,9 @@ class FormPreview(object):
return self.failed_hash(request) # Security hash failed. return self.failed_hash(request) # Security hash failed.
return self.done(request, f.clean_data) return self.done(request, f.clean_data)
else: else:
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state}) return render_to_response(self.form_template,
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
context_instance=RequestContext(request))
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ######################## # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################

View File

@ -1,5 +1,6 @@
from django.conf import settings from django.conf import settings
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from django.core.exceptions import SuspiciousOperation
from django.utils.cache import patch_vary_headers from django.utils.cache import patch_vary_headers
import datetime import datetime
@ -55,7 +56,7 @@ class SessionWrapper(object):
s = Session.objects.get(session_key=self.session_key, s = Session.objects.get(session_key=self.session_key,
expire_date__gt=datetime.datetime.now()) expire_date__gt=datetime.datetime.now())
self._session_cache = s.get_decoded() self._session_cache = s.get_decoded()
except Session.DoesNotExist: except (Session.DoesNotExist, SuspiciousOperation):
self._session_cache = {} self._session_cache = {}
# Set the session_key to None to force creation of a new # Set the session_key to None to force creation of a new
# key, for extra security. # key, for extra security.

View File

@ -6,8 +6,8 @@ class CacheClass(BaseCache):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
pass pass
def get(self, *args, **kwargs): def get(self, key, default=None):
pass return default
def set(self, *args, **kwargs): def set(self, *args, **kwargs):
pass pass
@ -16,7 +16,7 @@ class CacheClass(BaseCache):
pass pass
def get_many(self, *args, **kwargs): def get_many(self, *args, **kwargs):
pass return {}
def has_key(self, *args, **kwargs): def has_key(self, *args, **kwargs):
return False return False

View File

@ -60,7 +60,10 @@ class BaseHandler(object):
if response: if response:
return response return response
resolver = urlresolvers.RegexURLResolver(r'^/', settings.ROOT_URLCONF) # Get urlconf from request object, if available. Otherwise use default.
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
try: try:
callback, callback_args, callback_kwargs = resolver.resolve(request.path) callback, callback_args, callback_kwargs = resolver.resolve(request.path)

View File

@ -25,7 +25,7 @@ APP_ARGS = '[appname ...]'
# which has been installed. # which has been installed.
PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template') PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
INVALID_PROJECT_NAMES = ('django', 'test') INVALID_PROJECT_NAMES = ('django', 'site', 'test')
# Set up the terminal color scheme. # Set up the terminal color scheme.
class dummy: pass class dummy: pass
@ -708,7 +708,7 @@ def startproject(project_name, directory):
"Creates a Django project for the given project_name in the given directory." "Creates a Django project for the given project_name in the given directory."
from random import choice from random import choice
if project_name in INVALID_PROJECT_NAMES: if project_name in INVALID_PROJECT_NAMES:
sys.stderr.write(style.ERROR("Error: %r isn't a valid project name. Please try another.\n" % project_name)) sys.stderr.write(style.ERROR("Error: '%r' conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name.\n" % project_name))
sys.exit(1) sys.exit(1)
_start_helper('project', project_name, directory) _start_helper('project', project_name, directory)
# Create a random SECRET_KEY hash, and put it in the main settings. # Create a random SECRET_KEY hash, and put it in the main settings.

View File

@ -57,7 +57,7 @@ def Deserializer(object_list, **options):
for d in object_list: for d in object_list:
# Look up the model and starting build a dict of data for it. # Look up the model and starting build a dict of data for it.
Model = _get_model(d["model"]) Model = _get_model(d["model"])
data = {Model._meta.pk.name : d["pk"]} data = {Model._meta.pk.attname : d["pk"]}
m2m_data = {} m2m_data = {}
# Handle each field # Handle each field

View File

@ -21,6 +21,5 @@ DATA_TYPES = {
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'text', 'TextField': 'text',
'TimeField': 'time', 'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)', 'USStateField': 'varchar(2)',
} }

View File

@ -98,9 +98,11 @@ class DatabaseWrapper(local):
kwargs['port'] = int(settings.DATABASE_PORT) kwargs['port'] = int(settings.DATABASE_PORT)
kwargs.update(self.options) kwargs.update(self.options)
self.connection = Database.connect(**kwargs) self.connection = Database.connect(**kwargs)
cursor = self.connection.cursor() cursor = self.connection.cursor()
if self.connection.get_server_info() >= '4.1': if self.connection.get_server_info() >= '4.1':
cursor.execute("SET NAMES 'utf8'") cursor.execute("SET NAMES 'utf8'")
else:
cursor = self.connection.cursor()
if settings.DEBUG: if settings.DEBUG:
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self) return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
return cursor return cursor

View File

@ -25,6 +25,5 @@ DATA_TYPES = {
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'longtext', 'TextField': 'longtext',
'TimeField': 'time', 'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)', 'USStateField': 'varchar(2)',
} }

View File

@ -21,6 +21,5 @@ DATA_TYPES = {
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'long', 'TextField': 'long',
'TimeField': 'timestamp', 'TimeField': 'timestamp',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)', 'USStateField': 'varchar(2)',
} }

View File

@ -20,6 +20,38 @@ except ImportError:
# Import copy of _thread_local.py from Python 2.4 # Import copy of _thread_local.py from Python 2.4
from django.utils._threading_local import local from django.utils._threading_local import local
def smart_basestring(s, charset):
if isinstance(s, unicode):
return s.encode(charset)
return s
class UnicodeCursorWrapper(object):
"""
A thin wrapper around psycopg cursors that allows them to accept Unicode
strings as params.
This is necessary because psycopg doesn't apply any DB quoting to
parameters that are Unicode strings. If a param is Unicode, this will
convert it to a bytestring using DEFAULT_CHARSET before passing it to
psycopg.
"""
def __init__(self, cursor, charset):
self.cursor = cursor
self.charset = charset
def execute(self, sql, params=()):
return self.cursor.execute(sql, [smart_basestring(p, self.charset) for p in params])
def executemany(self, sql, param_list):
new_param_list = [tuple([smart_basestring(p, self.charset) for p in params]) for params in param_list]
return self.cursor.executemany(sql, new_param_list)
def __getattr__(self, attr):
if self.__dict__.has_key(attr):
return self.__dict__[attr]
else:
return getattr(self.cursor, attr)
class DatabaseWrapper(local): class DatabaseWrapper(local):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.connection = None self.connection = None
@ -45,6 +77,7 @@ class DatabaseWrapper(local):
self.connection.set_isolation_level(1) # make transactions transparent to all cursors self.connection.set_isolation_level(1) # make transactions transparent to all cursors
cursor = self.connection.cursor() cursor = self.connection.cursor()
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE]) cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
cursor = UnicodeCursorWrapper(cursor, settings.DEFAULT_CHARSET)
if settings.DEBUG: if settings.DEBUG:
return util.CursorDebugWrapper(cursor, self) return util.CursorDebugWrapper(cursor, self)
return cursor return cursor
@ -118,7 +151,7 @@ def get_pk_default_value():
try: try:
Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date)) Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
except AttributeError: except AttributeError:
raise Exception, "You appear to be using psycopg version 2, which isn't supported yet, because it's still in beta. Use psycopg version 1 instead: http://initd.org/projects/psycopg1" raise Exception, "You appear to be using psycopg version 2. Set your DATABASE_ENGINE to 'postgresql_psycopg2' instead of 'postgresql'."
Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time)) Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time))
Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp)) Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp))
Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean)) Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean))

View File

@ -25,6 +25,5 @@ DATA_TYPES = {
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'text', 'TextField': 'text',
'TimeField': 'time', 'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)', 'USStateField': 'varchar(2)',
} }

View File

@ -24,6 +24,5 @@ DATA_TYPES = {
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'text', 'TextField': 'text',
'TimeField': 'time', 'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)', 'USStateField': 'varchar(2)',
} }

View File

@ -2,7 +2,8 @@ from django.db.models import signals
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.conf import settings from django.conf import settings
from django.core import validators from django.core import validators
from django import forms from django import oldforms
from django import newforms as forms
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.functional import curry from django.utils.functional import curry
from django.utils.itercompat import tee from django.utils.itercompat import tee
@ -206,10 +207,10 @@ class Field(object):
if self.choices: if self.choices:
if self.radio_admin: if self.radio_admin:
field_objs = [forms.RadioSelectField] field_objs = [oldforms.RadioSelectField]
params['ul_class'] = get_ul_class(self.radio_admin) params['ul_class'] = get_ul_class(self.radio_admin)
else: else:
field_objs = [forms.SelectField] field_objs = [oldforms.SelectField]
params['choices'] = self.get_choices_default() params['choices'] = self.get_choices_default()
else: else:
@ -218,7 +219,7 @@ class Field(object):
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
""" """
Returns a list of forms.FormField instances for this field. It Returns a list of oldforms.FormField instances for this field. It
calculates the choices at runtime, not at compile time. calculates the choices at runtime, not at compile time.
name_prefix is a prefix to prepend to the "field_name" argument. name_prefix is a prefix to prepend to the "field_name" argument.
@ -333,6 +334,16 @@ class Field(object):
return self._choices return self._choices
choices = property(_get_choices) choices = property(_get_choices)
def formfield(self, **kwargs):
"Returns a django.newforms.Field instance for this database Field."
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.CharField(**defaults)
def value_from_object(self, obj):
"Returns the value of this field in the given model instance."
return getattr(obj, self.attname)
class AutoField(Field): class AutoField(Field):
empty_strings_allowed = False empty_strings_allowed = False
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -354,7 +365,7 @@ class AutoField(Field):
return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.HiddenField] return [oldforms.HiddenField]
def get_manipulator_new_data(self, new_data, rel=False): def get_manipulator_new_data(self, new_data, rel=False):
# Never going to be called # Never going to be called
@ -369,6 +380,9 @@ class AutoField(Field):
super(AutoField, self).contribute_to_class(cls, name) super(AutoField, self).contribute_to_class(cls, name)
cls._meta.has_auto_field = True cls._meta.has_auto_field = True
def formfield(self, **kwargs):
return None
class BooleanField(Field): class BooleanField(Field):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs['blank'] = True kwargs['blank'] = True
@ -381,11 +395,16 @@ class BooleanField(Field):
raise validators.ValidationError, gettext("This value must be either True or False.") raise validators.ValidationError, gettext("This value must be either True or False.")
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.CheckboxField] return [oldforms.CheckboxField]
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.BooleanField(**defaults)
class CharField(Field): class CharField(Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.TextField] return [oldforms.TextField]
def to_python(self, value): def to_python(self, value):
if isinstance(value, basestring): if isinstance(value, basestring):
@ -397,10 +416,15 @@ class CharField(Field):
raise validators.ValidationError, gettext_lazy("This field cannot be null.") raise validators.ValidationError, gettext_lazy("This field cannot be null.")
return str(value) return str(value)
def formfield(self, **kwargs):
defaults = {'max_length': self.maxlength, 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.CharField(**defaults)
# TODO: Maybe move this into contrib, because it's specialized. # TODO: Maybe move this into contrib, because it's specialized.
class CommaSeparatedIntegerField(CharField): class CommaSeparatedIntegerField(CharField):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.CommaSeparatedIntegerField] return [oldforms.CommaSeparatedIntegerField]
class DateField(Field): class DateField(Field):
empty_strings_allowed = False empty_strings_allowed = False
@ -462,12 +486,17 @@ class DateField(Field):
return Field.get_db_prep_save(self, value) return Field.get_db_prep_save(self, value)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.DateField] return [oldforms.DateField]
def flatten_data(self, follow, obj = None): def flatten_data(self, follow, obj=None):
val = self._get_val_from_obj(obj) val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')} return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.DateField(**defaults)
class DateTimeField(DateField): class DateTimeField(DateField):
def to_python(self, value): def to_python(self, value):
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
@ -503,7 +532,7 @@ class DateTimeField(DateField):
return Field.get_db_prep_lookup(self, lookup_type, value) return Field.get_db_prep_lookup(self, lookup_type, value)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.DateField, forms.TimeField] return [oldforms.DateField, oldforms.TimeField]
def get_manipulator_field_names(self, name_prefix): def get_manipulator_field_names(self, name_prefix):
return [name_prefix + self.name + '_date', name_prefix + self.name + '_time'] return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
@ -526,6 +555,11 @@ class DateTimeField(DateField):
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
time_field: (val is not None and val.strftime("%H:%M:%S") or '')} time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.DateTimeField(**defaults)
class EmailField(CharField): class EmailField(CharField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs['maxlength'] = 75 kwargs['maxlength'] = 75
@ -535,11 +569,16 @@ class EmailField(CharField):
return "CharField" return "CharField"
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.EmailField] return [oldforms.EmailField]
def validate(self, field_data, all_data): def validate(self, field_data, all_data):
validators.isValidEmail(field_data, all_data) validators.isValidEmail(field_data, all_data)
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.EmailField(**defaults)
class FileField(Field): class FileField(Field):
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs): def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
self.upload_to = upload_to self.upload_to = upload_to
@ -599,7 +638,7 @@ class FileField(Field):
os.remove(file_name) os.remove(file_name)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.FileUploadField, forms.HiddenField] return [oldforms.FileUploadField, oldforms.HiddenField]
def get_manipulator_field_names(self, name_prefix): def get_manipulator_field_names(self, name_prefix):
return [name_prefix + self.name + '_file', name_prefix + self.name] return [name_prefix + self.name + '_file', name_prefix + self.name]
@ -627,7 +666,7 @@ class FilePathField(Field):
Field.__init__(self, verbose_name, name, **kwargs) Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [curry(forms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)] return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
class FloatField(Field): class FloatField(Field):
empty_strings_allowed = False empty_strings_allowed = False
@ -636,7 +675,7 @@ class FloatField(Field):
Field.__init__(self, verbose_name, name, **kwargs) Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [curry(forms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)] return [curry(oldforms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
class ImageField(FileField): class ImageField(FileField):
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs): def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
@ -644,7 +683,7 @@ class ImageField(FileField):
FileField.__init__(self, verbose_name, name, **kwargs) FileField.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.ImageUploadField, forms.HiddenField] return [oldforms.ImageUploadField, oldforms.HiddenField]
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):
super(ImageField, self).contribute_to_class(cls, name) super(ImageField, self).contribute_to_class(cls, name)
@ -670,7 +709,12 @@ class ImageField(FileField):
class IntegerField(Field): class IntegerField(Field):
empty_strings_allowed = False empty_strings_allowed = False
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.IntegerField] return [oldforms.IntegerField]
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.IntegerField(**defaults)
class IPAddressField(Field): class IPAddressField(Field):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -678,7 +722,7 @@ class IPAddressField(Field):
Field.__init__(self, *args, **kwargs) Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.IPAddressField] return [oldforms.IPAddressField]
def validate(self, field_data, all_data): def validate(self, field_data, all_data):
validators.isValidIPAddress4(field_data, None) validators.isValidIPAddress4(field_data, None)
@ -689,22 +733,22 @@ class NullBooleanField(Field):
Field.__init__(self, *args, **kwargs) Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.NullBooleanField] return [oldforms.NullBooleanField]
class PhoneNumberField(IntegerField): class PhoneNumberField(IntegerField):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.PhoneNumberField] return [oldforms.PhoneNumberField]
def validate(self, field_data, all_data): def validate(self, field_data, all_data):
validators.isValidPhone(field_data, all_data) validators.isValidPhone(field_data, all_data)
class PositiveIntegerField(IntegerField): class PositiveIntegerField(IntegerField):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.PositiveIntegerField] return [oldforms.PositiveIntegerField]
class PositiveSmallIntegerField(IntegerField): class PositiveSmallIntegerField(IntegerField):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.PositiveSmallIntegerField] return [oldforms.PositiveSmallIntegerField]
class SlugField(Field): class SlugField(Field):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -716,15 +760,20 @@ class SlugField(Field):
Field.__init__(self, *args, **kwargs) Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.TextField] return [oldforms.TextField]
class SmallIntegerField(IntegerField): class SmallIntegerField(IntegerField):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.SmallIntegerField] return [oldforms.SmallIntegerField]
class TextField(Field): class TextField(Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.LargeTextField] return [oldforms.LargeTextField]
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'widget': forms.Textarea, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.CharField(**defaults)
class TimeField(Field): class TimeField(Field):
empty_strings_allowed = False empty_strings_allowed = False
@ -760,24 +809,39 @@ class TimeField(Field):
return Field.get_db_prep_save(self, value) return Field.get_db_prep_save(self, value)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.TimeField] return [oldforms.TimeField]
def flatten_data(self,follow, obj = None): def flatten_data(self,follow, obj = None):
val = self._get_val_from_obj(obj) val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')} return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
class URLField(Field): def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.TimeField(**defaults)
class URLField(CharField):
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
kwargs['maxlength'] = kwargs.get('maxlength', 200)
if verify_exists: if verify_exists:
kwargs.setdefault('validator_list', []).append(validators.isExistingURL) kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
Field.__init__(self, verbose_name, name, **kwargs) self.verify_exists = verify_exists
CharField.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.URLField] return [oldforms.URLField]
def get_internal_type(self):
return "CharField"
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'verify_exists': self.verify_exists, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.URLField(**defaults)
class USStateField(Field): class USStateField(Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [forms.USStateField] return [oldforms.USStateField]
class XMLField(TextField): class XMLField(TextField):
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs): def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
@ -788,7 +852,7 @@ class XMLField(TextField):
return "TextField" return "TextField"
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [curry(forms.XMLLargeTextField, schema_path=self.schema_path)] return [curry(oldforms.XMLLargeTextField, schema_path=self.schema_path)]
class OrderingField(IntegerField): class OrderingField(IntegerField):
empty_strings_allowed=False empty_strings_allowed=False
@ -801,4 +865,4 @@ class OrderingField(IntegerField):
return "IntegerField" return "IntegerField"
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
return [forms.HiddenField(name_prefix + self.name)] return [oldforms.HiddenField(name_prefix + self.name)]

View File

@ -2,7 +2,7 @@
Classes allowing "generic" relations through ContentType and object-id fields. Classes allowing "generic" relations through ContentType and object-id fields.
""" """
from django import forms from django import oldforms
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import backend from django.db import backend
from django.db.models import signals from django.db.models import signals
@ -98,7 +98,7 @@ class GenericRelation(RelatedField, Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
choices = self.get_choices_default() choices = self.get_choices_default()
return [curry(forms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)] return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
def get_choices_default(self): def get_choices_default(self):
return Field.get_choices(self, include_blank=False) return Field.get_choices(self, include_blank=False)

View File

@ -2,10 +2,12 @@ from django.db import backend, transaction
from django.db.models import signals, get_model from django.db.models import signals, get_model
from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class
from django.db.models.related import RelatedObject from django.db.models.related import RelatedObject
from django.utils.text import capfirst
from django.utils.translation import gettext_lazy, string_concat, ngettext from django.utils.translation import gettext_lazy, string_concat, ngettext
from django.utils.functional import curry from django.utils.functional import curry
from django.core import validators from django.core import validators
from django import forms from django import oldforms
from django import newforms as forms
from django.dispatch import dispatcher from django.dispatch import dispatcher
# For Python 2.3 # For Python 2.3
@ -256,8 +258,7 @@ class ForeignRelatedObjectsDescriptor(object):
# Otherwise, just move the named objects into the set. # Otherwise, just move the named objects into the set.
if self.related.field.null: if self.related.field.null:
manager.clear() manager.clear()
for obj in value: manager.add(*value)
manager.add(obj)
def create_many_related_manager(superclass): def create_many_related_manager(superclass):
"""Creates a manager that subclasses 'superclass' (which is a Manager) """Creates a manager that subclasses 'superclass' (which is a Manager)
@ -315,28 +316,36 @@ def create_many_related_manager(superclass):
# join_table: name of the m2m link table # join_table: name of the m2m link table
# source_col_name: the PK colname in join_table for the source object # source_col_name: the PK colname in join_table for the source object
# target_col_name: the PK colname in join_table for the target object # target_col_name: the PK colname in join_table for the target object
# *objs - objects to add # *objs - objects to add. Either object instances, or primary keys of object instances.
from django.db import connection from django.db import connection
# Add the newly created or already existing objects to the join table. # If there aren't any objects, there is nothing to do.
# First find out which items are already added, to avoid adding them twice if objs:
new_ids = set([obj._get_pk_val() for obj in objs]) # Check that all the objects are of the right type
cursor = connection.cursor() new_ids = set()
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \ for obj in objs:
(target_col_name, self.join_table, source_col_name, if isinstance(obj, self.model):
target_col_name, ",".join(['%s'] * len(new_ids))), new_ids.add(obj._get_pk_val())
[self._pk_val] + list(new_ids)) else:
if cursor.rowcount is not None and cursor.rowcount != 0: new_ids.add(obj)
existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)]) # Add the newly created or already existing objects to the join table.
else: # First find out which items are already added, to avoid adding them twice
existing_ids = set() cursor = connection.cursor()
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
(target_col_name, self.join_table, source_col_name,
target_col_name, ",".join(['%s'] * len(new_ids))),
[self._pk_val] + list(new_ids))
if cursor.rowcount is not None and cursor.rowcount != 0:
existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)])
else:
existing_ids = set()
# Add the ones that aren't there already # Add the ones that aren't there already
for obj_id in (new_ids - existing_ids): for obj_id in (new_ids - existing_ids):
cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \ cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
(self.join_table, source_col_name, target_col_name), (self.join_table, source_col_name, target_col_name),
[self._pk_val, obj_id]) [self._pk_val, obj_id])
transaction.commit_unless_managed() transaction.commit_unless_managed()
def _remove_items(self, source_col_name, target_col_name, *objs): def _remove_items(self, source_col_name, target_col_name, *objs):
# source_col_name: the PK colname in join_table for the source object # source_col_name: the PK colname in join_table for the source object
@ -344,16 +353,22 @@ def create_many_related_manager(superclass):
# *objs - objects to remove # *objs - objects to remove
from django.db import connection from django.db import connection
for obj in objs: # If there aren't any objects, there is nothing to do.
if not isinstance(obj, self.model): if objs:
raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name # Check that all the objects are of the right type
# Remove the specified objects from the join table old_ids = set()
cursor = connection.cursor() for obj in objs:
for obj in objs: if isinstance(obj, self.model):
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \ old_ids.add(obj._get_pk_val())
(self.join_table, source_col_name, target_col_name), else:
[self._pk_val, obj._get_pk_val()]) old_ids.add(obj)
transaction.commit_unless_managed() # Remove the specified objects from the join table
cursor = connection.cursor()
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
(self.join_table, source_col_name,
target_col_name, ",".join(['%s'] * len(old_ids))),
[self._pk_val] + list(old_ids))
transaction.commit_unless_managed()
def _clear_items(self, source_col_name): def _clear_items(self, source_col_name):
# source_col_name: the PK colname in join_table for the source object # source_col_name: the PK colname in join_table for the source object
@ -405,8 +420,7 @@ class ManyRelatedObjectsDescriptor(object):
manager = self.__get__(instance) manager = self.__get__(instance)
manager.clear() manager.clear()
for obj in value: manager.add(*value)
manager.add(obj)
class ReverseManyRelatedObjectsDescriptor(object): class ReverseManyRelatedObjectsDescriptor(object):
# This class provides the functionality that makes the related-object # This class provides the functionality that makes the related-object
@ -447,8 +461,7 @@ class ReverseManyRelatedObjectsDescriptor(object):
manager = self.__get__(instance) manager = self.__get__(instance)
manager.clear() manager.clear()
for obj in value: manager.add(*value)
manager.add(obj)
class ForeignKey(RelatedField, Field): class ForeignKey(RelatedField, Field):
empty_strings_allowed = False empty_strings_allowed = False
@ -493,13 +506,13 @@ class ForeignKey(RelatedField, Field):
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator)) params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
else: else:
if self.radio_admin: if self.radio_admin:
field_objs = [forms.RadioSelectField] field_objs = [oldforms.RadioSelectField]
params['ul_class'] = get_ul_class(self.radio_admin) params['ul_class'] = get_ul_class(self.radio_admin)
else: else:
if self.null: if self.null:
field_objs = [forms.NullSelectField] field_objs = [oldforms.NullSelectField]
else: else:
field_objs = [forms.SelectField] field_objs = [oldforms.SelectField]
params['choices'] = self.get_choices_default() params['choices'] = self.get_choices_default()
return field_objs, params return field_objs, params
@ -508,7 +521,7 @@ class ForeignKey(RelatedField, Field):
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField): if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
return rel_field.get_manipulator_field_objs() return rel_field.get_manipulator_field_objs()
else: else:
return [forms.IntegerField] return [oldforms.IntegerField]
def get_db_prep_save(self, value): def get_db_prep_save(self, value):
if value == '' or value == None: if value == '' or value == None:
@ -539,6 +552,11 @@ class ForeignKey(RelatedField, Field):
def contribute_to_related_class(self, cls, related): def contribute_to_related_class(self, cls, related):
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related)) setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
def formfield(self, **kwargs):
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.ChoiceField(**defaults)
class OneToOneField(RelatedField, IntegerField): class OneToOneField(RelatedField, IntegerField):
def __init__(self, to, to_field=None, **kwargs): def __init__(self, to, to_field=None, **kwargs):
try: try:
@ -581,13 +599,13 @@ class OneToOneField(RelatedField, IntegerField):
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator)) params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
else: else:
if self.radio_admin: if self.radio_admin:
field_objs = [forms.RadioSelectField] field_objs = [oldforms.RadioSelectField]
params['ul_class'] = get_ul_class(self.radio_admin) params['ul_class'] = get_ul_class(self.radio_admin)
else: else:
if self.null: if self.null:
field_objs = [forms.NullSelectField] field_objs = [oldforms.NullSelectField]
else: else:
field_objs = [forms.SelectField] field_objs = [oldforms.SelectField]
params['choices'] = self.get_choices_default() params['choices'] = self.get_choices_default()
return field_objs, params return field_objs, params
@ -600,6 +618,11 @@ class OneToOneField(RelatedField, IntegerField):
if not cls._meta.one_to_one_field: if not cls._meta.one_to_one_field:
cls._meta.one_to_one_field = self cls._meta.one_to_one_field = self
def formfield(self, **kwargs):
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.ChoiceField(**kwargs)
class ManyToManyField(RelatedField, Field): class ManyToManyField(RelatedField, Field):
def __init__(self, to, **kwargs): def __init__(self, to, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', None) kwargs['verbose_name'] = kwargs.get('verbose_name', None)
@ -610,6 +633,7 @@ class ManyToManyField(RelatedField, Field):
limit_choices_to=kwargs.pop('limit_choices_to', None), limit_choices_to=kwargs.pop('limit_choices_to', None),
raw_id_admin=kwargs.pop('raw_id_admin', False), raw_id_admin=kwargs.pop('raw_id_admin', False),
symmetrical=kwargs.pop('symmetrical', True)) symmetrical=kwargs.pop('symmetrical', True))
self.db_table = kwargs.pop('db_table', None)
if kwargs["rel"].raw_id_admin: if kwargs["rel"].raw_id_admin:
kwargs.setdefault("validator_list", []).append(self.isValidIDList) kwargs.setdefault("validator_list", []).append(self.isValidIDList)
Field.__init__(self, **kwargs) Field.__init__(self, **kwargs)
@ -622,17 +646,20 @@ class ManyToManyField(RelatedField, Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
if self.rel.raw_id_admin: if self.rel.raw_id_admin:
return [forms.RawIdAdminField] return [oldforms.RawIdAdminField]
else: else:
choices = self.get_choices_default() choices = self.get_choices_default()
return [curry(forms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)] return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
def get_choices_default(self): def get_choices_default(self):
return Field.get_choices(self, include_blank=False) return Field.get_choices(self, include_blank=False)
def _get_m2m_db_table(self, opts): def _get_m2m_db_table(self, opts):
"Function that can be curried to provide the m2m table name for this relation" "Function that can be curried to provide the m2m table name for this relation"
return '%s_%s' % (opts.db_table, self.name) if self.db_table:
return self.db_table
else:
return '%s_%s' % (opts.db_table, self.name)
def _get_m2m_column_name(self, related): def _get_m2m_column_name(self, related):
"Function that can be curried to provide the source column name for the m2m table" "Function that can be curried to provide the source column name for the m2m table"
@ -706,6 +733,19 @@ class ManyToManyField(RelatedField, Field):
def set_attributes_from_rel(self): def set_attributes_from_rel(self):
pass pass
def value_from_object(self, obj):
"Returns the value of this field in the given model instance."
return getattr(obj, self.attname).all()
def formfield(self, **kwargs):
# If initial is passed in, it's a list of related objects, but the
# MultipleChoiceField takes a list of IDs.
if kwargs.get('initial') is not None:
kwargs['initial'] = [i._get_pk_val() for i in kwargs['initial']]
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.MultipleChoiceField(**defaults)
class ManyToOneRel(object): class ManyToOneRel(object):
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None, def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False, max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,

View File

@ -1,4 +1,4 @@
from django.db.models.query import QuerySet from django.db.models.query import QuerySet, EmptyQuerySet
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.db.models import signals from django.db.models import signals
from django.db.models.fields import FieldDoesNotExist from django.db.models.fields import FieldDoesNotExist
@ -41,12 +41,18 @@ class Manager(object):
####################### #######################
# PROXIES TO QUERYSET # # PROXIES TO QUERYSET #
####################### #######################
def get_empty_query_set(self):
return EmptyQuerySet(self.model)
def get_query_set(self): def get_query_set(self):
"""Returns a new QuerySet object. Subclasses can override this method """Returns a new QuerySet object. Subclasses can override this method
to easily customise the behaviour of the Manager. to easily customise the behaviour of the Manager.
""" """
return QuerySet(self.model) return QuerySet(self.model)
def none(self):
return self.get_empty_query_set()
def all(self): def all(self):
return self.get_query_set() return self.get_query_set()

View File

@ -1,5 +1,5 @@
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django import forms from django import oldforms
from django.core import validators from django.core import validators
from django.db.models.fields import FileField, AutoField from django.db.models.fields import FileField, AutoField
from django.dispatch import dispatcher from django.dispatch import dispatcher
@ -40,7 +40,7 @@ class ManipulatorDescriptor(object):
self.man._prepare(model) self.man._prepare(model)
return self.man return self.man
class AutomaticManipulator(forms.Manipulator): class AutomaticManipulator(oldforms.Manipulator):
def _prepare(cls, model): def _prepare(cls, model):
cls.model = model cls.model = model
cls.manager = model._default_manager cls.manager = model._default_manager
@ -76,7 +76,7 @@ class AutomaticManipulator(forms.Manipulator):
# Add field for ordering. # Add field for ordering.
if self.change and self.opts.get_ordered_objects(): if self.change and self.opts.get_ordered_objects():
self.fields.append(forms.CommaSeparatedIntegerField(field_name="order_")) self.fields.append(oldforms.CommaSeparatedIntegerField(field_name="order_"))
def save(self, new_data): def save(self, new_data):
# TODO: big cleanup when core fields go -> use recursive manipulators. # TODO: big cleanup when core fields go -> use recursive manipulators.
@ -308,7 +308,7 @@ def manipulator_validator_unique_together(field_name_list, opts, self, field_dat
def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_type, self, field_data, all_data): def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_type, self, field_data, all_data):
from django.db.models.fields.related import ManyToOneRel from django.db.models.fields.related import ManyToOneRel
date_str = all_data.get(date_field.get_manipulator_field_names('')[0], None) date_str = all_data.get(date_field.get_manipulator_field_names('')[0], None)
date_val = forms.DateField.html2python(date_str) date_val = oldforms.DateField.html2python(date_str)
if date_val is None: if date_val is None:
return # Date was invalid. This will be caught by another validator. return # Date was invalid. This will be caught by another validator.
lookup_kwargs = {'%s__year' % date_field.name: date_val.year} lookup_kwargs = {'%s__year' % date_field.name: date_val.year}

View File

@ -1,5 +1,6 @@
from django.db import backend, connection, transaction from django.db import backend, connection, transaction
from django.db.models.fields import DateField, FieldDoesNotExist from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models.fields.generic import GenericRelation
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
@ -25,6 +26,9 @@ QUERY_TERMS = (
# Larger values are slightly faster at the expense of more storage space. # Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100 GET_ITERATOR_CHUNK_SIZE = 100
class EmptyResultSet(Exception):
pass
#################### ####################
# HELPER FUNCTIONS # # HELPER FUNCTIONS #
#################### ####################
@ -168,7 +172,12 @@ class QuerySet(object):
extra_select = self._select.items() extra_select = self._select.items()
cursor = connection.cursor() cursor = connection.cursor()
select, sql, params = self._get_sql_clause()
try:
select, sql, params = self._get_sql_clause()
except EmptyResultSet:
raise StopIteration
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
fill_cache = self._select_related fill_cache = self._select_related
index_end = len(self.model._meta.fields) index_end = len(self.model._meta.fields)
@ -192,7 +201,12 @@ class QuerySet(object):
counter._offset = None counter._offset = None
counter._limit = None counter._limit = None
counter._select_related = False counter._select_related = False
select, sql, params = counter._get_sql_clause()
try:
select, sql, params = counter._get_sql_clause()
except EmptyResultSet:
return 0
cursor = connection.cursor() cursor = connection.cursor()
if self._distinct: if self._distinct:
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table), id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
@ -523,7 +537,12 @@ class ValuesQuerySet(QuerySet):
field_names = [f.attname for f in self.model._meta.fields] field_names = [f.attname for f in self.model._meta.fields]
cursor = connection.cursor() cursor = connection.cursor()
select, sql, params = self._get_sql_clause()
try:
select, sql, params = self._get_sql_clause()
except EmptyResultSet:
raise StopIteration
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
while 1: while 1:
@ -545,7 +564,12 @@ class DateQuerySet(QuerySet):
if self._field.null: if self._field.null:
self._where.append('%s.%s IS NOT NULL' % \ self._where.append('%s.%s IS NOT NULL' % \
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
select, sql, params = self._get_sql_clause()
try:
select, sql, params = self._get_sql_clause()
except EmptyResultSet:
raise StopIteration
sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \
(backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
backend.quote_name(self._field.column))), sql, self._order) backend.quote_name(self._field.column))), sql, self._order)
@ -562,6 +586,25 @@ class DateQuerySet(QuerySet):
c._kind = self._kind c._kind = self._kind
c._order = self._order c._order = self._order
return c return c
class EmptyQuerySet(QuerySet):
def __init__(self, model=None):
super(EmptyQuerySet, self).__init__(model)
self._result_cache = []
def iterator(self):
raise StopIteration
def count(self):
return 0
def delete(self):
pass
def _clone(self, klass=None, **kwargs):
c = super(EmptyQuerySet, self)._clone(klass, **kwargs)
c._result_cache = []
return c
class QOperator(object): class QOperator(object):
"Base class for QAnd and QOr" "Base class for QAnd and QOr"
@ -571,10 +614,14 @@ class QOperator(object):
def get_sql(self, opts): def get_sql(self, opts):
joins, where, params = SortedDict(), [], [] joins, where, params = SortedDict(), [], []
for val in self.args: for val in self.args:
joins2, where2, params2 = val.get_sql(opts) try:
joins.update(joins2) joins2, where2, params2 = val.get_sql(opts)
where.extend(where2) joins.update(joins2)
params.extend(params2) where.extend(where2)
params.extend(params2)
except EmptyResultSet:
if not isinstance(self, QOr):
raise EmptyResultSet
if where: if where:
return joins, ['(%s)' % self.operator.join(where)], params return joins, ['(%s)' % self.operator.join(where)], params
return joins, [], params return joins, [], params
@ -628,8 +675,11 @@ class QNot(Q):
self.q = q self.q = q
def get_sql(self, opts): def get_sql(self, opts):
joins, where, params = self.q.get_sql(opts) try:
where2 = ['(NOT (%s))' % " AND ".join(where)] joins, where, params = self.q.get_sql(opts)
where2 = ['(NOT (%s))' % " AND ".join(where)]
except EmptyResultSet:
return SortedDict(), [], []
return joins, where2, params return joins, where2, params
def get_where_clause(lookup_type, table_prefix, field_name, value): def get_where_clause(lookup_type, table_prefix, field_name, value):
@ -641,7 +691,11 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
except KeyError: except KeyError:
pass pass
if lookup_type == 'in': if lookup_type == 'in':
return '%s%s IN (%s)' % (table_prefix, field_name, ','.join(['%s' for v in value])) in_string = ','.join(['%s' for id in value])
if in_string:
return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
else:
raise EmptyResultSet
elif lookup_type == 'range': elif lookup_type == 'range':
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name) return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
elif lookup_type in ('year', 'month', 'day'): elif lookup_type in ('year', 'month', 'day'):
@ -926,18 +980,26 @@ def delete_objects(seen_objs):
pk_list = [pk for pk,instance in seen_objs[cls]] pk_list = [pk for pk,instance in seen_objs[cls]]
for related in cls._meta.get_all_related_many_to_many_objects(): for related in cls._meta.get_all_related_many_to_many_objects():
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): if not isinstance(related.field, GenericRelation):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
(qn(related.field.m2m_db_table()), cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
qn(related.field.m2m_reverse_name()), (qn(related.field.m2m_db_table()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), qn(related.field.m2m_reverse_name()),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
for f in cls._meta.many_to_many: for f in cls._meta.many_to_many:
if isinstance(f, GenericRelation):
from django.contrib.contenttypes.models import ContentType
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
args_extra = [ContentType.objects.get_for_model(cls).id]
else:
query_extra = ''
args_extra = []
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(f.m2m_db_table()), qn(f.m2m_column_name()), (qn(f.m2m_db_table()), qn(f.m2m_column_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra,
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
for field in cls._meta.fields: for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_objs: if field.rel and field.null and field.rel.to in seen_objs:
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):

File diff suppressed because it is too large Load Diff

View File

@ -13,5 +13,5 @@ TODO:
from util import ValidationError from util import ValidationError
from widgets import * from widgets import *
from fields import * from fields import *
from forms import Form from forms import *
from models import * from models import *

View File

@ -0,0 +1 @@
from widgets import *

View File

@ -0,0 +1,59 @@
"""
Extra HTML Widget classes
"""
from django.newforms.widgets import Widget, Select
from django.utils.dates import MONTHS
import datetime
__all__ = ('SelectDateWidget',)
class SelectDateWidget(Widget):
"""
A Widget that splits date input into three <select> boxes.
This also serves as an example of a Widget that has more than one HTML
element and hence implements value_from_datadict.
"""
month_field = '%s_month'
day_field = '%s_day'
year_field = '%s_year'
def __init__(self, attrs=None, years=None):
# years is an optional list/tuple of years to use in the "year" select box.
self.attrs = attrs or {}
if years:
self.years = years
else:
this_year = datetime.date.today().year
self.years = range(this_year, this_year+10)
def render(self, name, value, attrs=None):
try:
value = datetime.date(*map(int, value.split('-')))
year_val, month_val, day_val = value.year, value.month, value.day
except (AttributeError, TypeError, ValueError):
year_val = month_val = day_val = None
output = []
month_choices = MONTHS.items()
month_choices.sort()
select_html = Select(choices=month_choices).render(self.month_field % name, month_val)
output.append(select_html)
day_choices = [(i, i) for i in range(1, 32)]
select_html = Select(choices=day_choices).render(self.day_field % name, day_val)
output.append(select_html)
year_choices = [(i, i) for i in self.years]
select_html = Select(choices=year_choices).render(self.year_field % name, year_val)
output.append(select_html)
return u'\n'.join(output)
def value_from_datadict(self, data, name):
y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name)
if y and m and d:
return '%s-%s-%s' % (y, m, d)
return None

View File

@ -3,8 +3,8 @@ Field classes
""" """
from django.utils.translation import gettext from django.utils.translation import gettext
from util import ValidationError, smart_unicode from util import ErrorList, ValidationError, smart_unicode
from widgets import TextInput, CheckboxInput, Select, SelectMultiple from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
import datetime import datetime
import re import re
import time import time
@ -12,10 +12,12 @@ import time
__all__ = ( __all__ = (
'Field', 'CharField', 'IntegerField', 'Field', 'CharField', 'IntegerField',
'DEFAULT_DATE_INPUT_FORMATS', 'DateField', 'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
'RegexField', 'EmailField', 'URLField', 'BooleanField', 'RegexField', 'EmailField', 'URLField', 'BooleanField',
'ChoiceField', 'MultipleChoiceField', 'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
'ComboField', 'ComboField', 'MultiValueField',
'SplitDateTimeField',
) )
# These values, if given to to_python(), will trigger the self.required check. # These values, if given to to_python(), will trigger the self.required check.
@ -28,15 +30,37 @@ except NameError:
class Field(object): class Field(object):
widget = TextInput # Default widget to use when rendering this type of Field. widget = TextInput # Default widget to use when rendering this type of Field.
hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
# Tracks each time a Field instance is created. Used to retain order. # Tracks each time a Field instance is created. Used to retain order.
creation_counter = 0 creation_counter = 0
def __init__(self, required=True, widget=None): def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None):
self.required = required # required -- Boolean that specifies whether the field is required.
# True by default.
# widget -- A Widget class, or instance of a Widget class, that should be
# used for this Field when displaying it. Each Field has a default
# Widget that it'll use if you don't specify this. In most cases,
# the default widget is TextInput.
# label -- A verbose name for this field, for use in displaying this field in
# a form. By default, Django will use a "pretty" version of the form
# field name, if the Field is part of a Form.
# initial -- A value to use in this Field's initial display. This value is
# *not* used as a fallback if data isn't given.
# help_text -- An optional string to use as "help text" for this Field.
if label is not None:
label = smart_unicode(label)
self.required, self.label, self.initial = required, label, initial
self.help_text = help_text
widget = widget or self.widget widget = widget or self.widget
if isinstance(widget, type): if isinstance(widget, type):
widget = widget() widget = widget()
# Hook into self.widget_attrs() for any Field-specific HTML attributes.
extra_attrs = self.widget_attrs(widget)
if extra_attrs:
widget.attrs.update(extra_attrs)
self.widget = widget self.widget = widget
# Increase the creation counter, and save our local copy. # Increase the creation counter, and save our local copy.
@ -54,15 +78,24 @@ class Field(object):
raise ValidationError(gettext(u'This field is required.')) raise ValidationError(gettext(u'This field is required.'))
return value return value
def widget_attrs(self, widget):
"""
Given a Widget instance (*not* a Widget class), returns a dictionary of
any HTML attributes that should be added to the Widget, based on this
Field.
"""
return {}
class CharField(Field): class CharField(Field):
def __init__(self, max_length=None, min_length=None, required=True, widget=None): def __init__(self, max_length=None, min_length=None, *args, **kwargs):
Field.__init__(self, required, widget)
self.max_length, self.min_length = max_length, min_length self.max_length, self.min_length = max_length, min_length
super(CharField, self).__init__(*args, **kwargs)
def clean(self, value): def clean(self, value):
"Validates max_length and min_length. Returns a Unicode object." "Validates max_length and min_length. Returns a Unicode object."
Field.clean(self, value) super(CharField, self).clean(value)
if value in EMPTY_VALUES: value = u'' if value in EMPTY_VALUES:
return u''
value = smart_unicode(value) value = smart_unicode(value)
if self.max_length is not None and len(value) > self.max_length: if self.max_length is not None and len(value) > self.max_length:
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length) raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
@ -70,19 +103,32 @@ class CharField(Field):
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length) raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
return value return value
def widget_attrs(self, widget):
if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
return {'maxlength': str(self.max_length)}
class IntegerField(Field): class IntegerField(Field):
def __init__(self, max_value=None, min_value=None, *args, **kwargs):
self.max_value, self.min_value = max_value, min_value
super(IntegerField, self).__init__(*args, **kwargs)
def clean(self, value): def clean(self, value):
""" """
Validates that int() can be called on the input. Returns the result Validates that int() can be called on the input. Returns the result
of int(). of int(). Returns None for empty values.
""" """
super(IntegerField, self).clean(value) super(IntegerField, self).clean(value)
if not self.required and value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return None
try: try:
return int(value) value = int(value)
except (ValueError, TypeError): except (ValueError, TypeError):
raise ValidationError(gettext(u'Enter a whole number.')) raise ValidationError(gettext(u'Enter a whole number.'))
if self.max_value is not None and value > self.max_value:
raise ValidationError(gettext(u'Ensure this value is less than or equal to %s.') % self.max_value)
if self.min_value is not None and value < self.min_value:
raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value)
return value
DEFAULT_DATE_INPUT_FORMATS = ( DEFAULT_DATE_INPUT_FORMATS = (
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
@ -93,8 +139,8 @@ DEFAULT_DATE_INPUT_FORMATS = (
) )
class DateField(Field): class DateField(Field):
def __init__(self, input_formats=None, required=True, widget=None): def __init__(self, input_formats=None, *args, **kwargs):
Field.__init__(self, required, widget) super(DateField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
def clean(self, value): def clean(self, value):
@ -102,7 +148,7 @@ class DateField(Field):
Validates that the input can be converted to a date. Returns a Python Validates that the input can be converted to a date. Returns a Python
datetime.date object. datetime.date object.
""" """
Field.clean(self, value) super(DateField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return None return None
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
@ -116,6 +162,33 @@ class DateField(Field):
continue continue
raise ValidationError(gettext(u'Enter a valid date.')) raise ValidationError(gettext(u'Enter a valid date.'))
DEFAULT_TIME_INPUT_FORMATS = (
'%H:%M:%S', # '14:30:59'
'%H:%M', # '14:30'
)
class TimeField(Field):
def __init__(self, input_formats=None, *args, **kwargs):
super(TimeField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
def clean(self, value):
"""
Validates that the input can be converted to a time. Returns a Python
datetime.time object.
"""
super(TimeField, self).clean(value)
if value in EMPTY_VALUES:
return None
if isinstance(value, datetime.time):
return value
for format in self.input_formats:
try:
return datetime.time(*time.strptime(value, format)[3:6])
except ValueError:
continue
raise ValidationError(gettext(u'Enter a valid time.'))
DEFAULT_DATETIME_INPUT_FORMATS = ( DEFAULT_DATETIME_INPUT_FORMATS = (
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
'%Y-%m-%d %H:%M', # '2006-10-25 14:30' '%Y-%m-%d %H:%M', # '2006-10-25 14:30'
@ -129,8 +202,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
) )
class DateTimeField(Field): class DateTimeField(Field):
def __init__(self, input_formats=None, required=True, widget=None): def __init__(self, input_formats=None, *args, **kwargs):
Field.__init__(self, required, widget) super(DateTimeField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
def clean(self, value): def clean(self, value):
@ -138,7 +211,7 @@ class DateTimeField(Field):
Validates that the input can be converted to a datetime. Returns a Validates that the input can be converted to a datetime. Returns a
Python datetime.datetime object. Python datetime.datetime object.
""" """
Field.clean(self, value) super(DateTimeField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return None return None
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
@ -153,16 +226,17 @@ class DateTimeField(Field):
raise ValidationError(gettext(u'Enter a valid date/time.')) raise ValidationError(gettext(u'Enter a valid date/time.'))
class RegexField(Field): class RegexField(Field):
def __init__(self, regex, error_message=None, required=True, widget=None): def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
""" """
regex can be either a string or a compiled regular expression object. regex can be either a string or a compiled regular expression object.
error_message is an optional error message to use, if error_message is an optional error message to use, if
'Enter a valid value' is too generic for you. 'Enter a valid value' is too generic for you.
""" """
Field.__init__(self, required, widget) super(RegexField, self).__init__(*args, **kwargs)
if isinstance(regex, basestring): if isinstance(regex, basestring):
regex = re.compile(regex) regex = re.compile(regex)
self.regex = regex self.regex = regex
self.max_length, self.min_length = max_length, min_length
self.error_message = error_message or gettext(u'Enter a valid value.') self.error_message = error_message or gettext(u'Enter a valid value.')
def clean(self, value): def clean(self, value):
@ -170,11 +244,16 @@ class RegexField(Field):
Validates that the input matches the regular expression. Returns a Validates that the input matches the regular expression. Returns a
Unicode object. Unicode object.
""" """
Field.clean(self, value) super(RegexField, self).clean(value)
if value in EMPTY_VALUES: value = u'' if value in EMPTY_VALUES:
value = u''
value = smart_unicode(value) value = smart_unicode(value)
if not self.required and value == u'': if value == u'':
return value return value
if self.max_length is not None and len(value) > self.max_length:
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
if self.min_length is not None and len(value) < self.min_length:
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
if not self.regex.search(value): if not self.regex.search(value):
raise ValidationError(self.error_message) raise ValidationError(self.error_message)
return value return value
@ -185,8 +264,9 @@ email_re = re.compile(
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
class EmailField(RegexField): class EmailField(RegexField):
def __init__(self, required=True, widget=None): def __init__(self, max_length=None, min_length=None, *args, **kwargs):
RegexField.__init__(self, email_re, gettext(u'Enter a valid e-mail address.'), required, widget) RegexField.__init__(self, email_re, max_length, min_length,
gettext(u'Enter a valid e-mail address.'), *args, **kwargs)
url_re = re.compile( url_re = re.compile(
r'^https?://' # http:// or https:// r'^https?://' # http:// or https://
@ -202,14 +282,16 @@ except ImportError:
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)' URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
class URLField(RegexField): class URLField(RegexField):
def __init__(self, required=True, verify_exists=False, widget=None, def __init__(self, max_length=None, min_length=None, verify_exists=False,
validator_user_agent=URL_VALIDATOR_USER_AGENT): validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
RegexField.__init__(self, url_re, gettext(u'Enter a valid URL.'), required, widget) super(URLField, self).__init__(url_re, max_length, min_length, gettext(u'Enter a valid URL.'), *args, **kwargs)
self.verify_exists = verify_exists self.verify_exists = verify_exists
self.user_agent = validator_user_agent self.user_agent = validator_user_agent
def clean(self, value): def clean(self, value):
value = RegexField.clean(self, value) value = super(URLField, self).clean(value)
if value == u'':
return value
if self.verify_exists: if self.verify_exists:
import urllib2 import urllib2
from django.conf import settings from django.conf import settings
@ -234,24 +316,43 @@ class BooleanField(Field):
def clean(self, value): def clean(self, value):
"Returns a Python boolean object." "Returns a Python boolean object."
Field.clean(self, value) super(BooleanField, self).clean(value)
return bool(value) return bool(value)
class NullBooleanField(BooleanField):
"""
A field whose valid values are None, True and False. Invalid values are
cleaned to None.
"""
widget = NullBooleanSelect
def clean(self, value):
return {True: True, False: False}.get(value, None)
class ChoiceField(Field): class ChoiceField(Field):
def __init__(self, choices=(), required=True, widget=Select): def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None, help_text=None):
if isinstance(widget, type): super(ChoiceField, self).__init__(required, widget, label, initial, help_text)
widget = widget(choices=choices)
Field.__init__(self, required, widget)
self.choices = choices self.choices = choices
def _get_choices(self):
return self._choices
def _set_choices(self, value):
# Setting choices also sets the choices on the widget.
self._choices = value
self.widget.choices = value
choices = property(_get_choices, _set_choices)
def clean(self, value): def clean(self, value):
""" """
Validates that the input is in self.choices. Validates that the input is in self.choices.
""" """
value = Field.clean(self, value) value = super(ChoiceField, self).clean(value)
if value in EMPTY_VALUES: value = u'' if value in EMPTY_VALUES:
value = u''
value = smart_unicode(value) value = smart_unicode(value)
if not self.required and value == u'': if value == u'':
return value return value
valid_values = set([str(k) for k, v in self.choices]) valid_values = set([str(k) for k, v in self.choices])
if value not in valid_values: if value not in valid_values:
@ -259,8 +360,10 @@ class ChoiceField(Field):
return value return value
class MultipleChoiceField(ChoiceField): class MultipleChoiceField(ChoiceField):
def __init__(self, choices=(), required=True, widget=SelectMultiple): hidden_widget = MultipleHiddenInput
ChoiceField.__init__(self, choices, required, widget)
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None, help_text=None):
super(MultipleChoiceField, self).__init__(choices, required, widget, label, initial, help_text)
def clean(self, value): def clean(self, value):
""" """
@ -284,8 +387,11 @@ class MultipleChoiceField(ChoiceField):
return new_value return new_value
class ComboField(Field): class ComboField(Field):
def __init__(self, fields=(), required=True, widget=None): """
Field.__init__(self, required, widget) A Field whose clean() method calls multiple Field clean() methods.
"""
def __init__(self, fields=(), *args, **kwargs):
super(ComboField, self).__init__(*args, **kwargs)
# Set 'required' to False on the individual fields, because the # Set 'required' to False on the individual fields, because the
# required validation will be handled by ComboField, not by those # required validation will be handled by ComboField, not by those
# individual fields. # individual fields.
@ -298,7 +404,88 @@ class ComboField(Field):
Validates the given value against all of self.fields, which is a Validates the given value against all of self.fields, which is a
list of Field instances. list of Field instances.
""" """
Field.clean(self, value) super(ComboField, self).clean(value)
for field in self.fields: for field in self.fields:
value = field.clean(value) value = field.clean(value)
return value return value
class MultiValueField(Field):
"""
A Field that is composed of multiple Fields.
Its clean() method takes a "decompressed" list of values. Each value in
this list is cleaned by the corresponding field -- the first value is
cleaned by the first field, the second value is cleaned by the second
field, etc. Once all fields are cleaned, the list of clean values is
"compressed" into a single value.
Subclasses should implement compress(), which specifies how a list of
valid values should be converted to a single value. Subclasses should not
have to implement clean().
You'll probably want to use this with MultiWidget.
"""
def __init__(self, fields=(), *args, **kwargs):
super(MultiValueField, self).__init__(*args, **kwargs)
# Set 'required' to False on the individual fields, because the
# required validation will be handled by MultiValueField, not by those
# individual fields.
for f in fields:
f.required = False
self.fields = fields
def clean(self, value):
"""
Validates every value in the given list. A value is validated against
the corresponding Field in self.fields.
For example, if this MultiValueField was instantiated with
fields=(DateField(), TimeField()), clean() would call
DateField.clean(value[0]) and TimeField.clean(value[1]).
"""
clean_data = []
errors = ErrorList()
if self.required and not value:
raise ValidationError(gettext(u'This field is required.'))
elif not self.required and not value:
return self.compress([])
if not isinstance(value, (list, tuple)):
raise ValidationError(gettext(u'Enter a list of values.'))
for i, field in enumerate(self.fields):
try:
field_value = value[i]
except KeyError:
field_value = None
if self.required and field_value in EMPTY_VALUES:
raise ValidationError(gettext(u'This field is required.'))
try:
clean_data.append(field.clean(field_value))
except ValidationError, e:
# Collect all validation errors in a single list, which we'll
# raise at the end of clean(), rather than raising a single
# exception for the first error we encounter.
errors.extend(e.messages)
if errors:
raise ValidationError(errors)
return self.compress(clean_data)
def compress(self, data_list):
"""
Returns a single value for the given list of values. The values can be
assumed to be valid.
For example, if this MultiValueField was instantiated with
fields=(DateField(), TimeField()), this might return a datetime
object created by combining the date and time in data_list.
"""
raise NotImplementedError('Subclasses must implement this method.')
class SplitDateTimeField(MultiValueField):
def __init__(self, *args, **kwargs):
fields = (DateField(), TimeField())
super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
def compress(self, data_list):
if data_list:
return datetime.datetime.combine(*data_list)
return None

View File

@ -2,11 +2,13 @@
Form classes Form classes
""" """
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict, MultiValueDict
from django.utils.html import escape from django.utils.html import escape
from fields import Field from fields import Field
from widgets import TextInput, Textarea, HiddenInput from widgets import TextInput, Textarea, HiddenInput, MultipleHiddenInput
from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError from util import flatatt, StrAndUnicode, ErrorDict, ErrorList, ValidationError
__all__ = ('BaseForm', 'Form')
NON_FIELD_ERRORS = '__all__' NON_FIELD_ERRORS = '__all__'
@ -24,24 +26,35 @@ class SortedDictFromList(SortedDict):
self.keyOrder = [d[0] for d in data] self.keyOrder = [d[0] for d in data]
dict.__init__(self, dict(data)) dict.__init__(self, dict(data))
def copy(self):
return SortedDictFromList(self.items())
class DeclarativeFieldsMetaclass(type): class DeclarativeFieldsMetaclass(type):
"Metaclass that converts Field attributes to a dictionary called 'fields'." "Metaclass that converts Field attributes to a dictionary called 'base_fields'."
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)] fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
attrs['fields'] = SortedDictFromList(fields) attrs['base_fields'] = SortedDictFromList(fields)
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
class Form(StrAndUnicode): class BaseForm(StrAndUnicode):
"A collection of Fields, plus their associated data." # This is the main implementation of all the Form logic. Note that this
__metaclass__ = DeclarativeFieldsMetaclass # class is different than Form. See the comments by the Form class for more
# information. Any improvements to the form API should be made to *this*
def __init__(self, data=None, auto_id=False): # TODO: prefix stuff # class, not to the Form class.
self.ignore_errors = data is None def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):
self.is_bound = data is not None
self.data = data or {} self.data = data or {}
self.auto_id = auto_id self.auto_id = auto_id
self.clean_data = None # Stores the data after clean() has been called. self.prefix = prefix
self.initial = initial or {}
self.__errors = None # Stores the errors after clean() has been called. self.__errors = None # Stores the errors after clean() has been called.
# The base_fields class attribute is the *class-wide* definition of
# fields. Because a particular *instance* of the class might want to
# alter self.fields, we create self.fields here by copying base_fields.
# Instances should always modify self.fields; they should not modify
# self.base_fields.
self.fields = self.base_fields.copy()
def __unicode__(self): def __unicode__(self):
return self.as_table() return self.as_table()
@ -70,9 +83,18 @@ class Form(StrAndUnicode):
Returns True if the form has no errors. Otherwise, False. If errors are Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False. being ignored, returns False.
""" """
return not self.ignore_errors and not bool(self.errors) return self.is_bound and not bool(self.errors)
def _html_output(self, normal_row, error_row, row_ender, errors_on_separate_row): def add_prefix(self, field_name):
"""
Returns the field name with a prefix appended, if this Form has a
prefix set.
Subclasses may wish to override.
"""
return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name
def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
top_errors = self.non_field_errors() # Errors that should be displayed above all fields. top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
output, hidden_fields = [], [] output, hidden_fields = [], []
@ -86,7 +108,12 @@ class Form(StrAndUnicode):
else: else:
if errors_on_separate_row and bf_errors: if errors_on_separate_row and bf_errors:
output.append(error_row % bf_errors) output.append(error_row % bf_errors)
output.append(normal_row % {'errors': bf_errors, 'label': bf.label_tag(escape(bf.verbose_name+':')), 'field': bf}) label = bf.label and bf.label_tag(escape(bf.label + ':')) or ''
if field.help_text:
help_text = help_text_html % field.help_text
else:
help_text = u''
output.append(normal_row % {'errors': bf_errors, 'label': label, 'field': unicode(bf), 'help_text': help_text})
if top_errors: if top_errors:
output.insert(0, error_row % top_errors) output.insert(0, error_row % top_errors)
if hidden_fields: # Insert any hidden fields in the last row. if hidden_fields: # Insert any hidden fields in the last row.
@ -101,15 +128,15 @@ class Form(StrAndUnicode):
def as_table(self): def as_table(self):
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>." "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
return self._html_output(u'<tr><td>%(label)s</td><td>%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', True) return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
def as_ul(self): def as_ul(self):
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
return self._html_output(u'<li>%(errors)s%(label)s %(field)s</li>', u'<li>%s</li>', '</li>', False) return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
def as_p(self): def as_p(self):
"Returns this form rendered as HTML <p>s." "Returns this form rendered as HTML <p>s."
return self._html_output(u'<p>%(label)s %(field)s</p>', u'<p>%s</p>', '</p>', True) return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'<p>%s</p>', '</p>', u' %s', True)
def non_field_errors(self): def non_field_errors(self):
""" """
@ -123,16 +150,16 @@ class Form(StrAndUnicode):
""" """
Cleans all of self.data and populates self.__errors and self.clean_data. Cleans all of self.data and populates self.__errors and self.clean_data.
""" """
self.clean_data = {}
errors = ErrorDict() errors = ErrorDict()
if self.ignore_errors: # Stop further processing. if not self.is_bound: # Stop further processing.
self.__errors = errors self.__errors = errors
return return
self.clean_data = {}
for name, field in self.fields.items(): for name, field in self.fields.items():
# value_from_datadict() gets the data from the dictionary. # value_from_datadict() gets the data from the dictionary.
# Each widget type knows how to retrieve its own data, because some # Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields. # widgets split data over several HTML fields.
value = field.widget.value_from_datadict(self.data, name) value = field.widget.value_from_datadict(self.data, self.add_prefix(name))
try: try:
value = field.clean(value) value = field.clean(value)
self.clean_data[name] = value self.clean_data[name] = value
@ -146,7 +173,7 @@ class Form(StrAndUnicode):
except ValidationError, e: except ValidationError, e:
errors[NON_FIELD_ERRORS] = e.messages errors[NON_FIELD_ERRORS] = e.messages
if errors: if errors:
self.clean_data = None delattr(self, 'clean_data')
self.__errors = errors self.__errors = errors
def clean(self): def clean(self):
@ -158,12 +185,26 @@ class Form(StrAndUnicode):
""" """
return self.clean_data return self.clean_data
class Form(BaseForm):
"A collection of Fields, plus their associated data."
# This is a separate class from BaseForm in order to abstract the way
# self.fields is specified. This class (Form) is the one that does the
# fancy metaclass stuff purely for the semantic sugar -- it allows one
# to define a form using declarative syntax.
# BaseForm itself has no way of designating self.fields.
__metaclass__ = DeclarativeFieldsMetaclass
class BoundField(StrAndUnicode): class BoundField(StrAndUnicode):
"A Field plus data" "A Field plus data"
def __init__(self, form, field, name): def __init__(self, form, field, name):
self.form = form self.form = form
self.field = field self.field = field
self.name = name self.name = name
self.html_name = form.add_prefix(name)
if self.field.label is None:
self.label = pretty_name(name)
else:
self.label = self.field.label
def __unicode__(self): def __unicode__(self):
"Renders this field as an HTML widget." "Renders this field as an HTML widget."
@ -190,7 +231,11 @@ class BoundField(StrAndUnicode):
auto_id = self.auto_id auto_id = self.auto_id
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'): if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
attrs['id'] = auto_id attrs['id'] = auto_id
return widget.render(self.name, self.data, attrs=attrs) if not self.form.is_bound:
data = self.form.initial.get(self.name, self.field.initial)
else:
data = self.data
return widget.render(self.html_name, data, attrs=attrs)
def as_text(self, attrs=None): def as_text(self, attrs=None):
""" """
@ -206,28 +251,29 @@ class BoundField(StrAndUnicode):
""" """
Returns a string of HTML for representing this as an <input type="hidden">. Returns a string of HTML for representing this as an <input type="hidden">.
""" """
return self.as_widget(HiddenInput(), attrs) return self.as_widget(self.field.hidden_widget(), attrs)
def _data(self): def _data(self):
"Returns the data for this BoundField, or None if it wasn't given." """
return self.form.data.get(self.name, None) Returns the data for this BoundField, or None if it wasn't given.
"""
return self.field.widget.value_from_datadict(self.form.data, self.html_name)
data = property(_data) data = property(_data)
def _verbose_name(self): def label_tag(self, contents=None, attrs=None):
return pretty_name(self.name)
verbose_name = property(_verbose_name)
def label_tag(self, contents=None):
""" """
Wraps the given contents in a <label>, if the field has an ID attribute. Wraps the given contents in a <label>, if the field has an ID attribute.
Does not HTML-escape the contents. If contents aren't given, uses the Does not HTML-escape the contents. If contents aren't given, uses the
field's HTML-escaped verbose_name. field's HTML-escaped label.
If attrs are given, they're used as HTML attributes on the <label> tag.
""" """
contents = contents or escape(self.verbose_name) contents = contents or escape(self.label)
widget = self.field.widget widget = self.field.widget
id_ = widget.attrs.get('id') or self.auto_id id_ = widget.attrs.get('id') or self.auto_id
if id_: if id_:
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents) attrs = attrs and flatatt(attrs) or ''
contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents)
return contents return contents
def _is_hidden(self): def _is_hidden(self):
@ -242,8 +288,8 @@ class BoundField(StrAndUnicode):
""" """
auto_id = self.form.auto_id auto_id = self.form.auto_id
if auto_id and '%s' in str(auto_id): if auto_id and '%s' in str(auto_id):
return str(auto_id) % self.name return str(auto_id) % self.html_name
elif auto_id: elif auto_id:
return self.name return self.html_name
return '' return ''
auto_id = property(_auto_id) auto_id = property(_auto_id)

View File

@ -1,13 +1,98 @@
""" """
Helper functions for creating Forms from Django models and database field objects. Helper functions for creating Form classes from Django models
and database field objects.
""" """
__all__ = ('form_for_model', 'form_for_fields') from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
def form_for_model(model): __all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields')
"Returns a Form instance for the given Django model class."
raise NotImplementedError def model_save(self, commit=True):
"""
Creates and returns model instance according to self.clean_data.
This method is created for any form_for_model Form.
"""
if self.errors:
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
return save_instance(self, self._model(), commit)
def save_instance(form, instance, commit=True):
"""
Saves bound Form ``form``'s clean_data into model instance ``instance``.
Assumes ``form`` has a field for every non-AutoField database field in
``instance``. If commit=True, then the changes to ``instance`` will be
saved to the database. Returns ``instance``.
"""
from django.db import models
opts = instance.__class__._meta
if form.errors:
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
clean_data = form.clean_data
for f in opts.fields:
if isinstance(f, models.AutoField):
continue
setattr(instance, f.attname, clean_data[f.name])
if commit:
instance.save()
for f in opts.many_to_many:
setattr(instance, f.attname, clean_data[f.name])
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
# data will be lost. This happens because a many-to-many options cannot be
# set on an object until after it's saved. Maybe we should raise an
# exception in that case.
return instance
def make_instance_save(instance):
"Returns the save() method for a form_for_instance Form."
def save(self, commit=True):
return save_instance(self, instance, commit)
return save
def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield()):
"""
Returns a Form class for the given Django model class.
Provide ``form`` if you want to use a custom BaseForm subclass.
Provide ``formfield_callback`` if you want to define different logic for
determining the formfield for a given database field. It's a callable that
takes a database Field instance and returns a form Field instance.
"""
opts = model._meta
field_list = []
for f in opts.fields + opts.many_to_many:
formfield = formfield_callback(f)
if formfield:
field_list.append((f.name, formfield))
fields = SortedDictFromList(field_list)
return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save})
def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
"""
Returns a Form class for the given Django model instance.
Provide ``form`` if you want to use a custom BaseForm subclass.
Provide ``formfield_callback`` if you want to define different logic for
determining the formfield for a given database field. It's a callable that
takes a database Field instance, plus **kwargs, and returns a form Field
instance with the given kwargs (i.e. 'initial').
"""
model = instance.__class__
opts = model._meta
field_list = []
for f in opts.fields + opts.many_to_many:
current_value = f.value_from_object(instance)
formfield = formfield_callback(f, initial=current_value)
if formfield:
field_list.append((f.name, formfield))
fields = SortedDictFromList(field_list)
return type(opts.object_name + 'InstanceForm', (form,),
{'base_fields': fields, '_model': model, 'save': make_instance_save(instance)})
def form_for_fields(field_list): def form_for_fields(field_list):
"Returns a Form instance for the given list of Django database field instances." "Returns a Form class for the given list of Django database field instances."
raise NotImplementedError fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
return type('FormForFields', (BaseForm,), {'base_fields': fields})

View File

@ -1,4 +1,9 @@
from django.conf import settings from django.conf import settings
from django.utils.html import escape
# Converts a dictionary to a single string with key="value", XML-style with
# a leading space. Assumes keys do not need to be XML-escaped.
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
def smart_unicode(s): def smart_unicode(s):
if not isinstance(s, basestring): if not isinstance(s, basestring):

View File

@ -3,13 +3,16 @@ HTML Widget classes
""" """
__all__ = ( __all__ = (
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput', 'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',
'Textarea', 'CheckboxInput', 'FileInput', 'Textarea', 'CheckboxInput',
'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple', 'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
'MultiWidget', 'SplitDateTimeWidget',
) )
from util import StrAndUnicode, smart_unicode from util import flatatt, StrAndUnicode, smart_unicode
from django.utils.datastructures import MultiValueDict
from django.utils.html import escape from django.utils.html import escape
from django.utils.translation import gettext
from itertools import chain from itertools import chain
try: try:
@ -17,18 +20,19 @@ try:
except NameError: except NameError:
from sets import Set as set # Python 2.3 fallback from sets import Set as set # Python 2.3 fallback
# Converts a dictionary to a single string with key="value", XML-style with
# a leading space. Assumes keys do not need to be XML-escaped.
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
class Widget(object): class Widget(object):
requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
is_hidden = False # Determines whether this corresponds to an <input type="hidden">. is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
def __init__(self, attrs=None): def __init__(self, attrs=None):
self.attrs = attrs or {} self.attrs = attrs or {}
def render(self, name, value): def render(self, name, value, attrs=None):
"""
Returns this Widget rendered as HTML, as a Unicode string.
The 'value' given is not guaranteed to be valid input, so subclass
implementations should program defensively.
"""
raise NotImplementedError raise NotImplementedError
def build_attrs(self, extra_attrs=None, **kwargs): def build_attrs(self, extra_attrs=None, **kwargs):
@ -64,6 +68,7 @@ class Input(Widget):
type='radio', which are special). type='radio', which are special).
""" """
input_type = None # Subclasses must define this. input_type = None # Subclasses must define this.
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
if value is None: value = '' if value is None: value = ''
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
@ -80,6 +85,26 @@ class HiddenInput(Input):
input_type = 'hidden' input_type = 'hidden'
is_hidden = True is_hidden = True
class MultipleHiddenInput(HiddenInput):
"""
A widget that handles <input type="hidden"> for fields that have a list
of values.
"""
def __init__(self, attrs=None, choices=()):
# choices can be any iterable
self.attrs = attrs or {}
self.choices = choices
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
return u'\n'.join([(u'<input%s />' % flatatt(dict(value=smart_unicode(v), **final_attrs))) for v in value])
def value_from_datadict(self, data, name):
if isinstance(data, MultiValueDict):
return data.getlist(name)
return data.get(name, None)
class FileInput(Input): class FileInput(Input):
input_type = 'file' input_type = 'file'
@ -111,9 +136,11 @@ class CheckboxInput(Widget):
class Select(Widget): class Select(Widget):
def __init__(self, attrs=None, choices=()): def __init__(self, attrs=None, choices=()):
# choices can be any iterable
self.attrs = attrs or {} self.attrs = attrs or {}
self.choices = choices # choices can be any iterable, but we may need to render this widget
# multiple times. Thus, collapse it into a list so it can be consumed
# more than once.
self.choices = list(choices)
def render(self, name, value, attrs=None, choices=()): def render(self, name, value, attrs=None, choices=()):
if value is None: value = '' if value is None: value = ''
@ -127,8 +154,26 @@ class Select(Widget):
output.append(u'</select>') output.append(u'</select>')
return u'\n'.join(output) return u'\n'.join(output)
class NullBooleanSelect(Select):
"""
A Select Widget intended to be used with NullBooleanField.
"""
def __init__(self, attrs=None):
choices = ((u'1', gettext('Unknown')), (u'2', gettext('Yes')), (u'3', gettext('No')))
super(NullBooleanSelect, self).__init__(attrs, choices)
def render(self, name, value, attrs=None, choices=()):
try:
value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value]
except KeyError:
value = u'1'
return super(NullBooleanSelect, self).render(name, value, attrs, choices)
def value_from_datadict(self, data, name):
value = data.get(name, None)
return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
class SelectMultiple(Widget): class SelectMultiple(Widget):
requires_data_list = True
def __init__(self, attrs=None, choices=()): def __init__(self, attrs=None, choices=()):
# choices can be any iterable # choices can be any iterable
self.attrs = attrs or {} self.attrs = attrs or {}
@ -146,19 +191,25 @@ class SelectMultiple(Widget):
output.append(u'</select>') output.append(u'</select>')
return u'\n'.join(output) return u'\n'.join(output)
def value_from_datadict(self, data, name):
if isinstance(data, MultiValueDict):
return data.getlist(name)
return data.get(name, None)
class RadioInput(StrAndUnicode): class RadioInput(StrAndUnicode):
"An object used by RadioFieldRenderer that represents a single <input type='radio'>." "An object used by RadioFieldRenderer that represents a single <input type='radio'>."
def __init__(self, name, value, attrs, choice, index): def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value self.name, self.value = name, value
self.attrs = attrs self.attrs = attrs
self.choice_value, self.choice_label = choice self.choice_value = smart_unicode(choice[0])
self.choice_label = smart_unicode(choice[1])
self.index = index self.index = index
def __unicode__(self): def __unicode__(self):
return u'<label>%s %s</label>' % (self.tag(), self.choice_label) return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
def is_checked(self): def is_checked(self):
return self.value == smart_unicode(self.choice_value) return self.value == self.choice_value
def tag(self): def tag(self):
if self.attrs.has_key('id'): if self.attrs.has_key('id'):
@ -178,6 +229,10 @@ class RadioFieldRenderer(StrAndUnicode):
for i, choice in enumerate(self.choices): for i, choice in enumerate(self.choices):
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i) yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
def __unicode__(self): def __unicode__(self):
"Outputs a <ul> for this set of radio fields." "Outputs a <ul> for this set of radio fields."
return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % w for w in self]) return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % w for w in self])
@ -203,11 +258,16 @@ class RadioSelect(Select):
class CheckboxSelectMultiple(SelectMultiple): class CheckboxSelectMultiple(SelectMultiple):
def render(self, name, value, attrs=None, choices=()): def render(self, name, value, attrs=None, choices=()):
if value is None: value = [] if value is None: value = []
has_id = attrs and attrs.has_key('id')
final_attrs = self.build_attrs(attrs, name=name) final_attrs = self.build_attrs(attrs, name=name)
output = [u'<ul>'] output = [u'<ul>']
str_values = set([smart_unicode(v) for v in value]) # Normalize to strings. str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values) for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
for option_value, option_label in chain(self.choices, choices): # If an ID attribute was given, add a numeric index as a suffix,
# so that the checkboxes don't all have the same ID attribute.
if has_id:
final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
option_value = smart_unicode(option_value) option_value = smart_unicode(option_value)
rendered_cb = cb.render(name, option_value) rendered_cb = cb.render(name, option_value)
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label)))) output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label))))
@ -220,3 +280,66 @@ class CheckboxSelectMultiple(SelectMultiple):
id_ += '_0' id_ += '_0'
return id_ return id_
id_for_label = classmethod(id_for_label) id_for_label = classmethod(id_for_label)
class MultiWidget(Widget):
"""
A widget that is composed of multiple widgets.
Its render() method takes a "decompressed" list of values, not a single
value. Each value in this list is rendered in the corresponding widget --
the first value is rendered in the first widget, the second value is
rendered in the second widget, etc.
Subclasses should implement decompress(), which specifies how a single
value should be converted to a list of values. Subclasses should not
have to implement clean().
Subclasses may implement format_output(), which takes the list of rendered
widgets and returns HTML that formats them any way you'd like.
You'll probably want to use this with MultiValueField.
"""
def __init__(self, widgets, attrs=None):
self.widgets = [isinstance(w, type) and w() or w for w in widgets]
super(MultiWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
# value is a list of values, each corresponding to a widget
# in self.widgets.
if not isinstance(value, list):
value = self.decompress(value)
output = []
for i, widget in enumerate(self.widgets):
try:
widget_value = value[i]
except KeyError:
widget_value = None
output.append(widget.render(name + '_%s' % i, widget_value, attrs))
return self.format_output(output)
def value_from_datadict(self, data, name):
return [data.get(name + '_%s' % i) for i in range(len(self.widgets))]
def format_output(self, rendered_widgets):
return u''.join(rendered_widgets)
def decompress(self, value):
"""
Returns a list of decompressed values for the given compressed value.
The given value can be assumed to be valid, but not necessarily
non-empty.
"""
raise NotImplementedError('Subclasses must implement this method.')
class SplitDateTimeWidget(MultiWidget):
"""
A Widget that splits datetime input into two <input type="text"> boxes.
"""
def __init__(self, attrs=None):
widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs))
super(SplitDateTimeWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return [value.date(), value.time()]
return [None, None]

1010
django/oldforms/__init__.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,20 +4,29 @@
from django.template import loader from django.template import loader
from django.http import HttpResponse, Http404 from django.http import HttpResponse, Http404
from django.db.models.manager import Manager
def render_to_response(*args, **kwargs): 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, *args, **kwargs): def get_object_or_404(klass, *args, **kwargs):
if isinstance(klass, Manager):
manager = klass
klass = manager.model
else:
manager = klass._default_manager
try: try:
return klass._default_manager.get(*args, **kwargs) return manager.get(*args, **kwargs)
except klass.DoesNotExist: except klass.DoesNotExist:
raise Http404 raise Http404
def get_list_or_404(klass, *args, **kwargs): def get_list_or_404(klass, *args, **kwargs):
obj_list = list(klass._default_manager.filter(*args, **kwargs)) if isinstance(klass, Manager):
manager = klass
else:
manager = klass._default_manager
obj_list = list(manager.filter(*args, **kwargs))
if not obj_list: if not obj_list:
raise Http404 raise Http404
return obj_list return obj_list

View File

@ -27,20 +27,38 @@ def fix_ampersands(value):
from django.utils.html import fix_ampersands from django.utils.html import fix_ampersands
return fix_ampersands(value) return fix_ampersands(value)
def floatformat(text): def floatformat(text, arg=-1):
""" """
Displays a floating point number as 34.2 (with one decimal place) -- but If called without an argument, displays a floating point
only if there's a point to be displayed number as 34.2 -- but only if there's a point to be displayed.
With a positive numeric argument, it displays that many decimal places
always.
With a negative numeric argument, it will display that many decimal
places -- but only if there's places to be displayed.
Examples:
num1 = 34.23234
num2 = 34.00000
num1|floatformat results in 34.2
num2|floatformat is 34
num1|floatformat:3 is 34.232
num2|floatformat:3 is 34.000
num1|floatformat:-3 is 34.232
num2|floatformat:-3 is 34
""" """
try: try:
f = float(text) f = float(text)
except ValueError: except ValueError:
return '' return ''
try:
d = int(arg)
except ValueError:
return str(f)
m = f - int(f) m = f - int(f)
if m: if not m and d < 0:
return '%.1f' % f
else:
return '%d' % int(f) return '%d' % int(f)
else:
formatstr = '%%.%df' % abs(d)
return formatstr % f
def linenumbers(value): def linenumbers(value):
"Displays text with line numbers" "Displays text with line numbers"

View File

@ -70,7 +70,7 @@ class SortedDict(dict):
return self.keyOrder[:] return self.keyOrder[:]
def values(self): def values(self):
return [dict.__getitem__(self,k) for k in self.keyOrder] return [dict.__getitem__(self, k) for k in self.keyOrder]
def update(self, dict): def update(self, dict):
for k, v in dict.items(): for k, v in dict.items():
@ -81,6 +81,10 @@ class SortedDict(dict):
self.keyOrder.append(key) self.keyOrder.append(key)
return dict.setdefault(self, key, default) return dict.setdefault(self, key, default)
def value_for_index(self, index):
"Returns the value of the item at the given zero-based index."
return self[self.keyOrder[index]]
class MultiValueDictKeyError(KeyError): class MultiValueDictKeyError(KeyError):
pass pass

View File

@ -11,7 +11,7 @@ Usage:
>>> >>>
""" """
from django.utils.dates import MONTHS, MONTHS_AP, WEEKDAYS from django.utils.dates import MONTHS, MONTHS_3, MONTHS_AP, WEEKDAYS
from django.utils.tzinfo import LocalTimezone from django.utils.tzinfo import LocalTimezone
from calendar import isleap, monthrange from calendar import isleap, monthrange
import re, time import re, time
@ -147,7 +147,7 @@ class DateFormat(TimeFormat):
def M(self): def M(self):
"Month, textual, 3 letters; e.g. 'Jan'" "Month, textual, 3 letters; e.g. 'Jan'"
return MONTHS[self.data.month][0:3] return MONTHS_3[self.data.month].title()
def n(self): def n(self):
"Month without leading zeros; i.e. '1' to '12'" "Month without leading zeros; i.e. '1' to '12'"

View File

@ -1,4 +1,4 @@
simplejson 1.3 simplejson 1.5
Copyright (c) 2006 Bob Ippolito Copyright (c) 2006 Bob Ippolito
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of

View File

@ -27,6 +27,21 @@ Encoding basic Python object hierarchies::
>>> io.getvalue() >>> io.getvalue()
'["streaming API"]' '["streaming API"]'
Compact encoding::
>>> import simplejson
>>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
'[1,2,3,{"4":5,"6":7}]'
Pretty printing::
>>> import simplejson
>>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
{
"4": 5,
"6": 7
}
Decoding JSON:: Decoding JSON::
>>> import simplejson >>> import simplejson
@ -68,10 +83,10 @@ Extending JSONEncoder::
['[', '2.0', ', ', '1.0', ']'] ['[', '2.0', ', ', '1.0', ']']
Note that the JSON produced by this module is a subset of YAML, Note that the JSON produced by this module's default settings
so it may be used as a serializer for that as well. is a subset of YAML, so it may be used as a serializer for that as well.
""" """
__version__ = '1.3' __version__ = '1.5'
__all__ = [ __all__ = [
'dump', 'dumps', 'load', 'loads', 'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONEncoder', 'JSONDecoder', 'JSONEncoder',
@ -81,7 +96,7 @@ from django.utils.simplejson.decoder import JSONDecoder
from django.utils.simplejson.encoder import JSONEncoder from django.utils.simplejson.encoder import JSONEncoder
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, **kw): allow_nan=True, cls=None, indent=None, **kw):
""" """
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
``.write()``-supporting file-like object). ``.write()``-supporting file-like object).
@ -105,6 +120,10 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
in strict compliance of the JSON specification, instead of using the in strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
If ``indent`` is a non-negative integer, then JSON array elements and object
members will be pretty-printed with that indent level. An indent level
of 0 will only insert newlines. ``None`` is the most compact representation.
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with ``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg. the ``cls`` kwarg.
@ -112,7 +131,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
if cls is None: if cls is None:
cls = JSONEncoder cls = JSONEncoder
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, check_circular=check_circular, allow_nan=allow_nan, indent=indent,
**kw).iterencode(obj) **kw).iterencode(obj)
# could accelerate with writelines in some versions of Python, at # could accelerate with writelines in some versions of Python, at
# a debuggability cost # a debuggability cost
@ -120,7 +139,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
fp.write(chunk) fp.write(chunk)
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, **kw): allow_nan=True, cls=None, indent=None, separators=None, **kw):
""" """
Serialize ``obj`` to a JSON formatted ``str``. Serialize ``obj`` to a JSON formatted ``str``.
@ -141,14 +160,26 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
strict compliance of the JSON specification, instead of using the strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
If ``indent`` is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level. An indent
level of 0 will only insert newlines. ``None`` is the most compact
representation.
If ``separators`` is an ``(item_separator, dict_separator)`` tuple
then it will be used instead of the default ``(', ', ': ')`` separators.
``(',', ':')`` is the most compact JSON representation.
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with ``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg. the ``cls`` kwarg.
""" """
if cls is None: if cls is None:
cls = JSONEncoder cls = JSONEncoder
return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, return cls(
check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj) skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators,
**kw).encode(obj)
def load(fp, encoding=None, cls=None, object_hook=None, **kw): def load(fp, encoding=None, cls=None, object_hook=None, **kw):
""" """

View File

@ -127,6 +127,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
raise ValueError(errmsg("Expecting property name", s, end)) raise ValueError(errmsg("Expecting property name", s, end))
end += 1 end += 1
encoding = getattr(context, 'encoding', None) encoding = getattr(context, 'encoding', None)
iterscan = JSONScanner.iterscan
while True: while True:
key, end = scanstring(s, end, encoding) key, end = scanstring(s, end, encoding)
end = _w(s, end).end() end = _w(s, end).end()
@ -134,7 +135,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
raise ValueError(errmsg("Expecting : delimiter", s, end)) raise ValueError(errmsg("Expecting : delimiter", s, end))
end = _w(s, end + 1).end() end = _w(s, end + 1).end()
try: try:
value, end = JSONScanner.iterscan(s, idx=end).next() value, end = iterscan(s, idx=end, context=context).next()
except StopIteration: except StopIteration:
raise ValueError(errmsg("Expecting object", s, end)) raise ValueError(errmsg("Expecting object", s, end))
pairs[key] = value pairs[key] = value
@ -164,9 +165,10 @@ def JSONArray(match, context, _w=WHITESPACE.match):
nextchar = s[end:end + 1] nextchar = s[end:end + 1]
if nextchar == ']': if nextchar == ']':
return values, end + 1 return values, end + 1
iterscan = JSONScanner.iterscan
while True: while True:
try: try:
value, end = JSONScanner.iterscan(s, idx=end).next() value, end = iterscan(s, idx=end, context=context).next()
except StopIteration: except StopIteration:
raise ValueError(errmsg("Expecting object", s, end)) raise ValueError(errmsg("Expecting object", s, end))
values.append(value) values.append(value)

View File

@ -3,11 +3,11 @@ Implementation of JSONEncoder
""" """
import re import re
# this should match any kind of infinity
INFCHARS = re.compile(r'[infINF]')
ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]') ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
ESCAPE_DCT = { ESCAPE_DCT = {
# escape all forward slashes to prevent </script> attack
'/': '\\/',
'\\': '\\\\', '\\': '\\\\',
'"': '\\"', '"': '\\"',
'\b': '\\b', '\b': '\\b',
@ -16,31 +16,31 @@ ESCAPE_DCT = {
'\r': '\\r', '\r': '\\r',
'\t': '\\t', '\t': '\\t',
} }
for i in range(20): for i in range(0x20):
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,)) ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
# assume this produces an infinity on all machines (probably not guaranteed)
INFINITY = float('1e66666')
def floatstr(o, allow_nan=True): def floatstr(o, allow_nan=True):
s = str(o) # Check for specials. Note that this type of test is processor- and/or
# If the first non-sign is a digit then it's not a special value # platform-specific, so do tests which don't depend on the internals.
if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
return s if o != o:
elif not allow_nan: text = 'NaN'
elif o == INFINITY:
text = 'Infinity'
elif o == -INFINITY:
text = '-Infinity'
else:
return str(o)
if not allow_nan:
raise ValueError("Out of range float values are not JSON compliant: %r" raise ValueError("Out of range float values are not JSON compliant: %r"
% (o,)) % (o,))
# These are the string representations on the platforms I've tried
if s == 'nan': return text
return 'NaN'
if s == 'inf':
return 'Infinity'
if s == '-inf':
return '-Infinity'
# NaN should either be inequal to itself, or equal to everything
if o != o or o == 0.0:
return 'NaN'
# Last ditch effort, assume inf
if o < 0:
return '-Infinity'
return 'Infinity'
def encode_basestring(s): def encode_basestring(s):
""" """
@ -90,8 +90,11 @@ class JSONEncoder(object):
implementation (to raise ``TypeError``). implementation (to raise ``TypeError``).
""" """
__all__ = ['__init__', 'default', 'encode', 'iterencode'] __all__ = ['__init__', 'default', 'encode', 'iterencode']
item_separator = ', '
key_separator = ': '
def __init__(self, skipkeys=False, ensure_ascii=True, def __init__(self, skipkeys=False, ensure_ascii=True,
check_circular=True, allow_nan=True, sort_keys=False): check_circular=True, allow_nan=True, sort_keys=False,
indent=None, separators=None):
""" """
Constructor for JSONEncoder, with sensible defaults. Constructor for JSONEncoder, with sensible defaults.
@ -116,6 +119,15 @@ class JSONEncoder(object):
If sort_keys is True, then the output of dictionaries will be If sort_keys is True, then the output of dictionaries will be
sorted by key; this is useful for regression tests to ensure sorted by key; this is useful for regression tests to ensure
that JSON serializations can be compared on a day-to-day basis. that JSON serializations can be compared on a day-to-day basis.
If indent is a non-negative integer, then JSON array
elements and object members will be pretty-printed with that
indent level. An indent level of 0 will only insert newlines.
None is the most compact representation.
If specified, separators should be a (item_separator, key_separator)
tuple. The default is (', ', ': '). To get the most compact JSON
representation you should specify (',', ':') to eliminate whitespace.
""" """
self.skipkeys = skipkeys self.skipkeys = skipkeys
@ -123,6 +135,13 @@ class JSONEncoder(object):
self.check_circular = check_circular self.check_circular = check_circular
self.allow_nan = allow_nan self.allow_nan = allow_nan
self.sort_keys = sort_keys self.sort_keys = sort_keys
self.indent = indent
self.current_indent_level = 0
if separators is not None:
self.item_separator, self.key_separator = separators
def _newline_indent(self):
return '\n' + (' ' * (self.indent * self.current_indent_level))
def _iterencode_list(self, lst, markers=None): def _iterencode_list(self, lst, markers=None):
if not lst: if not lst:
@ -134,14 +153,25 @@ class JSONEncoder(object):
raise ValueError("Circular reference detected") raise ValueError("Circular reference detected")
markers[markerid] = lst markers[markerid] = lst
yield '[' yield '['
if self.indent is not None:
self.current_indent_level += 1
newline_indent = self._newline_indent()
separator = self.item_separator + newline_indent
yield newline_indent
else:
newline_indent = None
separator = self.item_separator
first = True first = True
for value in lst: for value in lst:
if first: if first:
first = False first = False
else: else:
yield ', ' yield separator
for chunk in self._iterencode(value, markers): for chunk in self._iterencode(value, markers):
yield chunk yield chunk
if newline_indent is not None:
self.current_indent_level -= 1
yield self._newline_indent()
yield ']' yield ']'
if markers is not None: if markers is not None:
del markers[markerid] del markers[markerid]
@ -156,6 +186,15 @@ class JSONEncoder(object):
raise ValueError("Circular reference detected") raise ValueError("Circular reference detected")
markers[markerid] = dct markers[markerid] = dct
yield '{' yield '{'
key_separator = self.key_separator
if self.indent is not None:
self.current_indent_level += 1
newline_indent = self._newline_indent()
item_separator = self.item_separator + newline_indent
yield newline_indent
else:
newline_indent = None
item_separator = self.item_separator
first = True first = True
if self.ensure_ascii: if self.ensure_ascii:
encoder = encode_basestring_ascii encoder = encode_basestring_ascii
@ -165,7 +204,7 @@ class JSONEncoder(object):
if self.sort_keys: if self.sort_keys:
keys = dct.keys() keys = dct.keys()
keys.sort() keys.sort()
items = [(k,dct[k]) for k in keys] items = [(k, dct[k]) for k in keys]
else: else:
items = dct.iteritems() items = dct.iteritems()
for key, value in items: for key, value in items:
@ -190,11 +229,14 @@ class JSONEncoder(object):
if first: if first:
first = False first = False
else: else:
yield ', ' yield item_separator
yield encoder(key) yield encoder(key)
yield ': ' yield key_separator
for chunk in self._iterencode(value, markers): for chunk in self._iterencode(value, markers):
yield chunk yield chunk
if newline_indent is not None:
self.current_indent_level -= 1
yield self._newline_indent()
yield '}' yield '}'
if markers is not None: if markers is not None:
del markers[markerid] del markers[markerid]

View File

@ -0,0 +1,40 @@
from django.utils import simplejson
import cgi
class JSONFilter(object):
def __init__(self, app, mime_type='text/x-json'):
self.app = app
self.mime_type = mime_type
def __call__(self, environ, start_response):
# Read JSON POST input to jsonfilter.json if matching mime type
response = {'status': '200 OK', 'headers': []}
def json_start_response(status, headers):
response['status'] = status
response['headers'].extend(headers)
environ['jsonfilter.mime_type'] = self.mime_type
if environ.get('REQUEST_METHOD', '') == 'POST':
if environ.get('CONTENT_TYPE', '') == self.mime_type:
args = [_ for _ in [environ.get('CONTENT_LENGTH')] if _]
data = environ['wsgi.input'].read(*map(int, args))
environ['jsonfilter.json'] = simplejson.loads(data)
res = simplejson.dumps(self.app(environ, json_start_response))
jsonp = cgi.parse_qs(environ.get('QUERY_STRING', '')).get('jsonp')
if jsonp:
content_type = 'text/javascript'
res = ''.join(jsonp + ['(', res, ')'])
elif 'Opera' in environ.get('HTTP_USER_AGENT', ''):
# Opera has bunk XMLHttpRequest support for most mime types
content_type = 'text/plain'
else:
content_type = self.mime_type
headers = [
('Content-type', content_type),
('Content-length', len(res)),
]
headers.extend(response['headers'])
start_response(response['status'], headers)
return [res]
def factory(app, global_conf, **kw):
return JSONFilter(app, **kw)

View File

@ -3,11 +3,12 @@ Iterator based sre token scanner
""" """
import sre_parse, sre_compile, sre_constants import sre_parse, sre_compile, sre_constants
from sre_constants import BRANCH, SUBPATTERN from sre_constants import BRANCH, SUBPATTERN
from re import VERBOSE, MULTILINE, DOTALL
import re import re
__all__ = ['Scanner', 'pattern'] __all__ = ['Scanner', 'pattern']
FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL) FLAGS = (VERBOSE | MULTILINE | DOTALL)
class Scanner(object): class Scanner(object):
def __init__(self, lexicon, flags=FLAGS): def __init__(self, lexicon, flags=FLAGS):
self.actions = [None] self.actions = [None]

View File

@ -8,17 +8,28 @@ capfirst = lambda x: x and x[0].upper() + x[1:]
def wrap(text, width): def wrap(text, width):
""" """
A word-wrap function that preserves existing line breaks and most spaces in A word-wrap function that preserves existing line breaks and most spaces in
the text. Expects that existing line breaks are posix newlines (\n). the text. Expects that existing line breaks are posix newlines.
See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
""" """
return reduce(lambda line, word, width=width: '%s%s%s' % def _generator():
(line, it = iter(text.split(' '))
' \n'[(len(line[line.rfind('\n')+1:]) word = it.next()
+ len(word.split('\n',1)[0] yield word
) >= width)], pos = len(word) - word.rfind('\n') - 1
word), for word in it:
text.split(' ') if "\n" in word:
) lines = word.splitlines()
else:
lines = (word,)
pos += len(lines[0]) + 1
if pos > width:
yield '\n'
pos = len(lines[-1])
else:
yield ' '
if len(lines) > 1:
pos = len(lines[-1])
yield word
return "".join(_generator())
def truncate_words(s, num): def truncate_words(s, num):
"Truncates a string after a certain number of words." "Truncates a string after a certain number of words."

View File

@ -1,6 +1,6 @@
from django.core.xheaders import populate_xheaders from django.core.xheaders import populate_xheaders
from django.template import loader from django.template import loader
from django import forms from django import oldforms
from django.db.models import FileField from django.db.models import FileField
from django.contrib.auth.views import redirect_to_login from django.contrib.auth.views import redirect_to_login
from django.template import RequestContext from django.template import RequestContext
@ -56,7 +56,7 @@ def create_object(request, model, template_name=None,
new_data = manipulator.flatten_data() new_data = manipulator.flatten_data()
# Create the FormWrapper, template, context, response # Create the FormWrapper, template, context, response
form = forms.FormWrapper(manipulator, new_data, errors) form = oldforms.FormWrapper(manipulator, new_data, errors)
if not template_name: if not template_name:
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
t = template_loader.get_template(template_name) t = template_loader.get_template(template_name)
@ -128,7 +128,7 @@ def update_object(request, model, object_id=None, slug=None,
# This makes sure the form acurate represents the fields of the place. # This makes sure the form acurate represents the fields of the place.
new_data = manipulator.flatten_data() new_data = manipulator.flatten_data()
form = forms.FormWrapper(manipulator, new_data, errors) form = oldforms.FormWrapper(manipulator, new_data, errors)
if not template_name: if not template_name:
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
t = template_loader.get_template(template_name) t = template_loader.get_template(template_name)

View File

@ -17,7 +17,7 @@ admin
The automatic Django administrative interface. For more information, see The automatic Django administrative interface. For more information, see
`Tutorial 2`_. `Tutorial 2`_.
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/ .. _Tutorial 2: ../tutorial2/
auth auth
==== ====
@ -26,7 +26,7 @@ Django's authentication framework.
See the `authentication documentation`_. See the `authentication documentation`_.
.. _authentication documentation: http://www.djangoproject.com/documentation/authentication/ .. _authentication documentation: ../authentication/
comments comments
======== ========
@ -46,7 +46,7 @@ A middleware for preventing Cross Site Request Forgeries
See the `csrf documentation`_. See the `csrf documentation`_.
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/ .. _csrf documentation: ../csrf/
formtools formtools
========= =========
@ -137,7 +137,7 @@ A framework for managing simple "flat" HTML content in a database.
See the `flatpages documentation`_. See the `flatpages documentation`_.
.. _flatpages documentation: http://www.djangoproject.com/documentation/flatpages/ .. _flatpages documentation: ../flatpages/
markup markup
====== ======
@ -157,7 +157,7 @@ A framework for managing redirects.
See the `redirects documentation`_. See the `redirects documentation`_.
.. _redirects documentation: http://www.djangoproject.com/documentation/redirects/ .. _redirects documentation: ../redirects/
sites sites
===== =====
@ -168,7 +168,7 @@ one or more sites.
See the `sites documentation`_. See the `sites documentation`_.
.. _sites documentation: http://www.djangoproject.com/documentation/sites/ .. _sites documentation: ../sites/
sitemaps sitemaps
======== ========
@ -177,7 +177,7 @@ A framework for generating Google sitemap XML files.
See the `sitemaps documentation`_. See the `sitemaps documentation`_.
.. _sitemaps documentation: http://www.djangoproject.com/documentation/sitemaps/ .. _sitemaps documentation: ../sitemaps/
syndication syndication
=========== ===========
@ -186,7 +186,7 @@ A framework for generating syndication feeds, in RSS and Atom, quite easily.
See the `syndication documentation`_. See the `syndication documentation`_.
.. _syndication documentation: http://www.djangoproject.com/documentation/syndication/ .. _syndication documentation: ../syndication/
Other add-ons Other add-ons
============= =============

View File

@ -65,7 +65,7 @@ are equivalent::
SetEnv DJANGO_SETTINGS_MODULE mysite.settings SetEnv DJANGO_SETTINGS_MODULE mysite.settings
PythonOption DJANGO_SETTINGS_MODULE mysite.settings PythonOption DJANGO_SETTINGS_MODULE mysite.settings
.. _authentication system: http://www.djangoproject.com/documentation/authentication/ .. _authentication system: ../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
.. _custom permissions: http://www.djangoproject.com/documentation/authentication/#custom-permissions .. _custom permissions: ../authentication/#custom-permissions

View File

@ -98,26 +98,26 @@ change:
rewrite before Django 1.0. Even if the change isn't quite that drastic, rewrite before Django 1.0. Even if the change isn't quite that drastic,
there will at least be moderate changes. there will at least be moderate changes.
.. _caching: http://www.djangoproject.com/documentation/cache/ .. _caching: ../cache/
.. _custom template tags and libraries: http://www.djangoproject.com/documentation/templates_python/ .. _custom template tags and libraries: ../templates_python/
.. _database lookup: http://www.djangoproject.com/documentation/db_api/ .. _database lookup: ../db_api/
.. _django-admin utility: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin utility: ../django_admin/
.. _fastcgi integration: http://www.djangoproject.com/documentation/fastcgi/ .. _fastcgi integration: ../fastcgi/
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/ .. _flatpages: ../flatpages/
.. _generic views: http://www.djangoproject.com/documentation/generic_views/ .. _generic views: ../generic_views/
.. _internationalization: http://www.djangoproject.com/documentation/i18n/ .. _internationalization: ../i18n/
.. _legacy database integration: http://www.djangoproject.com/documentation/legacy_databases/ .. _legacy database integration: ../legacy_databases/
.. _model definition: http://www.djangoproject.com/documentation/model_api/ .. _model definition: ../model_api/
.. _mod_python integration: http://www.djangoproject.com/documentation/modpython/ .. _mod_python integration: ../modpython/
.. _redirects: http://www.djangoproject.com/documentation/redirects/ .. _redirects: ../redirects/
.. _request/response objects: http://www.djangoproject.com/documentation/request_response/ .. _request/response objects: ../request_response/
.. _sending email: http://www.djangoproject.com/documentation/email/ .. _sending email: ../email/
.. _sessions: http://www.djangoproject.com/documentation/sessions/ .. _sessions: ../sessions/
.. _settings: http://www.djangoproject.com/documentation/settings/ .. _settings: ../settings/
.. _syndication: http://www.djangoproject.com/documentation/syndication/ .. _syndication: ../syndication/
.. _template language: http://www.djangoproject.com/documentation/templates/ .. _template language: ../templates/
.. _transactions: http://www.djangoproject.com/documentation/transactions/ .. _transactions: ../transactions/
.. _url dispatch: http://www.djangoproject.com/documentation/url_dispatch/ .. _url dispatch: ../url_dispatch/
.. _forms and validation: http://www.djangoproject.com/documentation/forms/ .. _forms and validation: ../forms/
.. _serialization: http://www.djangoproject.com/documentation/serialization/ .. _serialization: ../serialization/
.. _authentication: http://www.djangoproject.com/documentation/authentication/ .. _authentication: ../authentication/

View File

@ -144,8 +144,8 @@ custom methods:
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
doesn't allow profiles. doesn't allow profiles.
.. _Django model: http://www.djangoproject.com/documentation/model_api/ .. _Django model: ../model_api/
.. _DEFAULT_FROM_EMAIL: http://www.djangoproject.com/documentation/settings/#default-from-email .. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
Manager functions Manager functions
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
@ -271,8 +271,8 @@ previous section). You can tell them apart with ``is_authenticated()``, like so:
else: else:
# Do something for anonymous users. # Do something for anonymous users.
.. _request objects: http://www.djangoproject.com/documentation/request_response/#httprequest-objects .. _request objects: ../request_response/#httprequest-objects
.. _session documentation: http://www.djangoproject.com/documentation/sessions/ .. _session documentation: ../sessions/
How to log a user in How to log a user in
-------------------- --------------------
@ -441,8 +441,8 @@ block::
{% endblock %} {% endblock %}
.. _forms documentation: http://www.djangoproject.com/documentation/forms/ .. _forms documentation: ../forms/
.. _site framework docs: http://www.djangoproject.com/documentation/sites/ .. _site framework docs: ../sites/
Limiting access to logged-in users that pass a test Limiting access to logged-in users that pass a test
--------------------------------------------------- ---------------------------------------------------
@ -544,7 +544,7 @@ For example::
def limited_object_detail(*args, **kwargs): def limited_object_detail(*args, **kwargs):
return object_detail(*args, **kwargs) return object_detail(*args, **kwargs)
.. _generic view: http://www.djangoproject.com/documentation/generic_views/ .. _generic view: ../generic_views/
Permissions Permissions
=========== ===========
@ -606,7 +606,7 @@ This example model creates three custom permissions::
The only thing this does is create those extra permissions when you run The only thing this does is create those extra permissions when you run
``syncdb``. ``syncdb``.
.. _model Meta attribute: http://www.djangoproject.com/documentation/model_api/#meta-options .. _model Meta attribute: ../model_api/#meta-options
API reference API reference
------------- -------------
@ -645,7 +645,7 @@ The currently logged-in user and his/her permissions are made available in the
setting contains ``"django.core.context_processors.auth"``, which is default. setting contains ``"django.core.context_processors.auth"``, which is default.
For more, see the `RequestContext docs`_. For more, see the `RequestContext docs`_.
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-requestcontext .. _RequestContext docs: ../templates_python/#subclassing-context-requestcontext
Users Users
----- -----
@ -691,7 +691,7 @@ Thus, you can check permissions in template ``{% if %}`` statements::
<p>You don't have permission to do anything in the foo app.</p> <p>You don't have permission to do anything in the foo app.</p>
{% endif %} {% endif %}
.. _template context: http://www.djangoproject.com/documentation/templates_python/ .. _template context: ../templates_python/
Groups Groups
====== ======
@ -756,7 +756,7 @@ scenes, so any messages will be deleted even if you don't display them.
Finally, note that this messages framework only works with users in the user Finally, note that this messages framework only works with users in the user
database. To send messages to anonymous users, use the `session framework`_. database. To send messages to anonymous users, use the `session framework`_.
.. _session framework: http://www.djangoproject.com/documentation/sessions/ .. _session framework: ../sessions/
Other authentication sources Other authentication sources
============================ ============================

View File

@ -250,7 +250,7 @@ Additionally, ``CacheMiddleware`` automatically sets a few headers in each
See the `middleware documentation`_ for more on middleware. See the `middleware documentation`_ for more on middleware.
.. _`middleware documentation`: http://www.djangoproject.com/documentation/middleware/ .. _`middleware documentation`: ../middleware/
The per-view cache The per-view cache
================== ==================

View File

@ -22,6 +22,9 @@ of the community, so there are many ways you can help Django's development:
likely to be skeptical of large-scale suggestions without some code to likely to be skeptical of large-scale suggestions without some code to
back it up. back it up.
* Triage patches that have been submitted by other users. Please read
`Ticket triage`_ below, for details on the triage process.
That's all you need to know if you'd like to join the Django development That's all you need to know if you'd like to join the Django development
community. The rest of this document describes the details of how our community community. The rest of this document describes the details of how our community
works and how it handles bugs, mailing lists, and all the other minutiae of works and how it handles bugs, mailing lists, and all the other minutiae of
@ -44,8 +47,10 @@ particular:
* **Do** write complete, reproducible, specific bug reports. Include as * **Do** write complete, reproducible, specific bug reports. Include as
much information as you possibly can, complete with code snippets, test much information as you possibly can, complete with code snippets, test
cases, etc. A minimal example that illustrates the bug in a nice small cases, etc. This means including a clear, concise description of the
test case is the best possible bug report. problem, and a clear set of instructions for replicating the problem.
A minimal example that illustrates the bug in a nice small test case
is the best possible bug report.
* **Don't** use the ticket system to ask support questions. Use the * **Don't** use the ticket system to ask support questions. Use the
`django-users`_ list, or the `#django`_ IRC channel for that. `django-users`_ list, or the `#django`_ IRC channel for that.
@ -117,9 +122,99 @@ Patch style
* Name the patch file with a ``.diff`` extension; this will let the ticket * Name the patch file with a ``.diff`` extension; this will let the ticket
tracker apply correct syntax highlighting, which is quite helpful. tracker apply correct syntax highlighting, which is quite helpful.
* Put the prefix "[patch] " before the title of your ticket. This will make * Check the "Has patch" box on the ticket details. This will make it
it obvious that the ticket includes a patch, and it will add the ticket obvious that the ticket includes a patch, and it will add the ticket to
to the `list of tickets with patches`_. the `list of tickets with patches`_.
* The code required to fix a problem or add a feature is an essential part
of a patch, but it is not the only part. A good patch should also include
a regression test to validate the behavior that has been fixed (and prevent
the problem from arising again).
* If the code associated with a patch adds a new feature, or modifies behavior
of an existing feature, the patch should also contain documentation.
Non-trivial patches
-------------------
A "non-trivial" patch is one that is more than a simple bug fix. It's a patch
that introduces Django functionality and makes some sort of design decision.
If you provide a non-trivial patch, include evidence that alternatives have
been discussed on `django-developers`_. If you're not sure whether your patch
should be considered non-trivial, just ask.
Ticket triage
=============
Unfortunately, not all bug reports in the `ticket tracker`_ provide all
the `required details`_. A number of tickets have patches, but those patches
don't meet all the requirements of a `good patch`_.
One way to help out is to *triage* bugs that have been reported by other
users. A couple of dedicated volunteers work on this regularly, but more help
is always appreciated.
Most of the workflow is based around the concept of a ticket's "triage stage".
This stage describes where in its lifetime a given ticket is at any time.
Along with a handful of flags, this field easily tells us what and who each
ticket is waiting on.
Since a picture is worth a thousand words, let's start there:
.. image:: http://media.djangoproject.com/img/doc/djangotickets.png
:height: 451
:width: 590
:alt: Django's ticket workflow
We've got two roles here:
* Core developers: people with commit access who make the decisions and
write the bulk of the code.
* Ticket triagers: community members who keep track of tickets, making
sure the tickets are always categorized correctly.
Second, note the four triage stages:
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
examine the ticket and move it along.
2. "Design decision needed" means "this concept requires a design
decision," which should be discussed either in the ticket comments or on
django-developers.
3. Once a ticket is ruled to be approved for fixing, it's moved into the
"Accepted" stage. This stage is where all the real work gets done.
4. If a ticket has an associated patch (see below), a triager will review the
patch. If the patch is complete, it'll be marked as "ready for checkin" so
that a core developer knows to review and check in the patches.
The second part of this workflow involves a set of flags the describe what the
ticket has or needs in order to be "ready for checkin":
"Has patch"
The means the ticket has an associated patch_. These will be
reviewed to see if the patch is "good".
"Needs documentation"
This flag is used for tickets with patches that need associated
documentation. Complete documentation of features is a prerequisite
before we can check a fix into the codebase.
"Needs tests"
This flags the patch as needing associated unit tests. Again, this is a
required part of a valid patch.
"Patch needs improvement"
This flag means that although the ticket *has* a patch, it's not quite
ready for checkin. This could mean the patch no longer applies
cleanly, or that the code doesn't live up to our standards.
.. _required details: `Reporting bugs`_
.. _good patch: `Patch style`_
.. _patch: `Submitting patches`_
Submitting and maintaining translations Submitting and maintaining translations
======================================= =======================================
@ -137,7 +232,7 @@ translated, here's what to do:
`i18n documentation`_. `i18n documentation`_.
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/ .. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/ .. _i18n documentation: ../i18n/
Coding style Coding style
============ ============
@ -262,7 +357,7 @@ The Django tests all use the testing infrastructure that ships with Django for
testing applications. See `Testing Django applications`_ for an explanation of testing applications. See `Testing Django applications`_ for an explanation of
how to write new tests. how to write new tests.
.. _Testing Django applications: http://www.djangoproject.com/documentation/testing/ .. _Testing Django applications: ../testing/
Running the unit tests Running the unit tests
---------------------- ----------------------
@ -338,21 +433,63 @@ trunk more than once.
Using branches Using branches
-------------- --------------
To test a given branch, you can simply check out the entire branch, like so:: To use a branch, you'll need to do two things:
* Get the branch's code through Subversion.
* Point your Python ``site-packages`` directory at the branch's version of
the ``django`` package rather than the version you already have
installed.
Getting the code from Subversion
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the latest version of a branch's code, check it out using Subversion::
svn co http://code.djangoproject.com/svn/django/branches/<branch>/ svn co http://code.djangoproject.com/svn/django/branches/<branch>/
Or, if you've got a working directory you'd like to switch to use a branch, ...where ``<branch>`` is the branch's name. See the `list of branch names`_.
you can use::
Alternatively, you can automatically convert an existing directory of the
Django source code as long as you've checked it out via Subversion. To do the
conversion, execute this command from within your ``django`` directory::
svn switch http://code.djangoproject.com/svn/django/branches/<branch>/ svn switch http://code.djangoproject.com/svn/django/branches/<branch>/
...in the root of your Django sandbox (the directory that contains ``django``,
``docs``, and ``tests``).
The advantage of using ``svn switch`` instead of ``svn co`` is that the The advantage of using ``svn switch`` instead of ``svn co`` is that the
``switch`` command retains any changes you might have made to your local copy ``switch`` command retains any changes you might have made to your local copy
of the code. It attempts to merge those changes into the "switched" code. of the code. It attempts to merge those changes into the "switched" code. The
disadvantage is that it may cause conflicts with your local changes if the
"switched" code has altered the same lines of code.
(Note that if you use ``svn switch``, you don't need to point Python at the new
version, as explained in the next section.)
.. _list of branch names: http://code.djangoproject.com/browser/django/branches
Pointing Python at the new Django version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once you've retrieved the branch's code, you'll need to change your Python
``site-packages`` directory so that it points to the branch version of the
``django`` directory. (The ``site-packages`` directory is somewhere such as
``/usr/lib/python2.4/site-packages`` or
``/usr/local/lib/python2.4/site-packages`` or ``C:\Python\site-packages``.)
The simplest way to do this is by renaming the old ``django`` directory to
``django.OLD`` and moving the trunk version of the code into the directory
and calling it ``django``.
Alternatively, you can use a symlink called ``django`` that points to the
location of the branch's ``django`` package. If you want to switch back, just
change the symlink to point to the old code.
If you're using Django 0.95 or earlier and installed it using
``python setup.py install``, you'll have a directory called something like
``Django-0.95-py2.4.egg`` instead of ``django``. In this case, edit the file
``setuptools.pth`` and remove the line that references the Django ``.egg``
file. Then copy the branch's version of the ``django`` directory into
``site-packages``.
Official releases Official releases
================= =================
@ -457,8 +594,7 @@ requests for commit access are potential flame-war starters, and will be ignored
.. _search the tracker: http://code.djangoproject.com/search .. _search the tracker: http://code.djangoproject.com/search
.. _django-users: http://groups.google.com/group/django-users .. _django-users: http://groups.google.com/group/django-users
.. _`#django`: irc://irc.freenode.net/django .. _`#django`: irc://irc.freenode.net/django
.. _list of tickets with patches: http://code.djangoproject.com/report/12 .. _list of tickets with patches: http://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority
.. _PEP 8: http://www.python.org/peps/pep-0008.html .. _PEP 8: http://www.python.org/peps/pep-0008.html
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
.. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n .. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n
.. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases .. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases

View File

@ -1,9 +1,9 @@
===================================== =====================================
Cross Site Request Forgery Protection Cross Site Request Forgery protection
===================================== =====================================
The CsrfMiddleware class provides easy-to-use protection against The CsrfMiddleware class provides easy-to-use protection against
`Cross Site Request Forgeries`_. This type of attack occurs when a malicious `Cross Site Request Forgeries`_. This type of attack occurs when a malicious
web site creates a link or form button that is intended to perform some action web site creates a link or form button that is intended to perform some action
on your web site, using the credentials of a logged-in user who is tricked on your web site, using the credentials of a logged-in user who is tricked
into clicking on the link in their browser. into clicking on the link in their browser.
@ -12,12 +12,12 @@ The first defense against CSRF attacks is to ensure that GET requests
are side-effect free. POST requests can then be protected by adding this are side-effect free. POST requests can then be protected by adding this
middleware into your list of installed middleware. middleware into your list of installed middleware.
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF .. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
How to use it How to use it
============= =============
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
your list of middleware classes, ``MIDDLEWARE_CLASSES``. It needs to process your list of middleware classes, ``MIDDLEWARE_CLASSES``. It needs to process
the response after the SessionMiddleware, so must come before it in the the response after the SessionMiddleware, so must come before it in the
list. It also must process the response before things like compression list. It also must process the response before things like compression
@ -25,16 +25,17 @@ happen to the response, so it must come after GZipMiddleware in the list.
How it works How it works
============ ============
CsrfMiddleware does two things: CsrfMiddleware does two things:
1. It modifies outgoing requests by adding a hidden form field to all 1. It modifies outgoing requests by adding a hidden form field to all
'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is 'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is
a hash of the session ID plus a secret. If there is no session ID set, a hash of the session ID plus a secret. If there is no session ID set,
this modification of the response isn't done, so there is very little this modification of the response isn't done, so there is very little
performance penalty for those requests that don't have a session. performance penalty for those requests that don't have a session.
2. On all incoming POST requests that have the session cookie set, it 2. On all incoming POST requests that have the session cookie set, it
checks that the 'csrfmiddlewaretoken' is present and correct. If it checks that the 'csrfmiddlewaretoken' is present and correct. If it
isn't, the user will get a 403 error. isn't, the user will get a 403 error.
This ensures that only forms that have originated from your web site This ensures that only forms that have originated from your web site
@ -43,26 +44,26 @@ can be used to POST data back.
It deliberately only targets HTTP POST requests (and the corresponding It deliberately only targets HTTP POST requests (and the corresponding
POST forms). GET requests ought never to have side effects (if you are POST forms). GET requests ought never to have side effects (if you are
using HTTP GET and POST correctly), and so a CSRF attack with a GET using HTTP GET and POST correctly), and so a CSRF attack with a GET
request will always be harmless. request will always be harmless.
POST requests that are not accompanied by a session cookie are not protected, POST requests that are not accompanied by a session cookie are not protected,
but they do not need to be protected, since the 'attacking' web site but they do not need to be protected, since the 'attacking' web site
could make these kind of requests anyway. could make these kind of requests anyway.
The Content-Type is checked before modifying the response, and only The Content-Type is checked before modifying the response, and only
pages that are served as 'text/html' or 'application/xml+xhtml' pages that are served as 'text/html' or 'application/xml+xhtml'
are modified. are modified.
Limitations Limitations
=========== ===========
CsrfMiddleware requires Django's session framework to work. If you have CsrfMiddleware requires Django's session framework to work. If you have
a custom authentication system that manually sets cookies and the like, a custom authentication system that manually sets cookies and the like,
it won't help you. it won't help you.
If your app creates HTML pages and forms in some unusual way, (e.g. If your app creates HTML pages and forms in some unusual way, (e.g.
it sends fragments of HTML in javascript document.write statements) it sends fragments of HTML in javascript document.write statements)
you might bypass the filter that adds the hidden field to the form, you might bypass the filter that adds the hidden field to the form,
in which case form submission will always fail. It may still be possible in which case form submission will always fail. It may still be possible
to use the middleware, provided you can find some way to get the to use the middleware, provided you can find some way to get the
CSRF token and ensure that is included when your form is submitted. CSRF token and ensure that is included when your form is submitted.

View File

@ -143,9 +143,9 @@ or ``UPDATE`` SQL statements. Specifically, when you call ``save()``, Django
follows this algorithm: follows this algorithm:
* If the object's primary key attribute is set to a value that evaluates to * If the object's primary key attribute is set to a value that evaluates to
``False`` (such as ``None`` or the empty string), Django executes a ``True`` (i.e., a value other than ``None`` or the empty string), Django
``SELECT`` query to determine whether a record with the given primary key executes a ``SELECT`` query to determine whether a record with the given
already exists. primary key already exists.
* If the record with the given primary key does already exist, Django * If the record with the given primary key does already exist, Django
executes an ``UPDATE`` query. executes an ``UPDATE`` query.
* If the object's primary key attribute is *not* set, or if it's set but a * If the object's primary key attribute is *not* set, or if it's set but a
@ -525,6 +525,21 @@ Examples::
[datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)] [datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day') >>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.datetime(2005, 3, 20)] [datetime.datetime(2005, 3, 20)]
``none()``
~~~~~~~~~~
**New in Django development version**
Returns an ``EmptyQuerySet`` -- a ``QuerySet`` that always evaluates to
an empty list. This can be used in cases where you know that you should
return an empty result set and your caller is expecting a ``QuerySet``
object (instead of returning an empty list, for example.)
Examples::
>>> Entry.objects.none()
[]
``select_related()`` ``select_related()``
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
@ -1704,6 +1719,46 @@ For every ``ImageField``, the object will have ``get_FOO_height()`` and
``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This ``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This
returns the height (or width) of the image, as an integer, in pixels. returns the height (or width) of the image, as an integer, in pixels.
Shortcuts
=========
As you develop views, you will discover a number of common idioms in the
way you use the database API. Django encodes some of these idioms as
shortcuts that can be used to simplify the process of writing views.
get_object_or_404()
-------------------
One common idiom to use ``get()`` and raise ``Http404`` if the
object doesn't exist. This idiom is captured by ``get_object_or_404()``.
This function takes a Django model as its first argument and an
arbitrary number of keyword arguments, which it passes to the manager's
``get()`` function. It raises ``Http404`` if the object doesn't
exist. For example::
# Get the Entry with a primary key of 3
e = get_object_or_404(Entry, pk=3)
When you provide a model to this shortcut function, the default manager
is used to execute the underlying ``get()`` query. If you don't want to
use the default manager, or you want to search a list of related objects,
you can provide ``get_object_or_404()`` with a manager object, instead.
For example::
# Get the author of blog instance `e` with a name of 'Fred'
a = get_object_or_404(e.authors, name='Fred')
# Use a custom manager 'recent_entries' in the search for an
# entry with a primary key of 3
e = get_object_or_404(Entry.recent_entries, pk=3)
get_list_or_404()
-----------------
``get_list_or_404`` behaves the same was as ``get_object_or_404()``
-- except the it uses using ``filter()`` instead of ``get()``. It raises
``Http404`` if the list is empty.
Falling back to raw SQL Falling back to raw SQL
======================= =======================

View File

@ -186,7 +186,7 @@ code.
This is the philosophy behind `template inheritance`_. This is the philosophy behind `template inheritance`_.
.. _template inheritance: http://www.djangoproject.com/documentation/templates/#template-inheritance .. _template inheritance: ../templates/#template-inheritance
Be decoupled from HTML Be decoupled from HTML
---------------------- ----------------------

View File

@ -53,7 +53,7 @@ Prints the admin-index template snippet for the given appnames.
Use admin-index template snippets if you want to customize the look and feel of Use admin-index template snippets if you want to customize the look and feel of
your admin's index page. See `Tutorial 2`_ for more information. your admin's index page. See `Tutorial 2`_ for more information.
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/ .. _Tutorial 2: ../tutorial2/
createcachetable [tablename] createcachetable [tablename]
---------------------------- ----------------------------
@ -61,7 +61,7 @@ createcachetable [tablename]
Creates a cache table named ``tablename`` for use with the database cache Creates a cache table named ``tablename`` for use with the database cache
backend. See the `cache documentation`_ for more information. backend. See the `cache documentation`_ for more information.
.. _cache documentation: http://www.djangoproject.com/documentation/cache/ .. _cache documentation: ../cache/
dbshell dbshell
------- -------
@ -190,7 +190,7 @@ By default, the development server doesn't serve any static files for your site
you want to configure Django to serve static media, read the `serving static files`_ you want to configure Django to serve static media, read the `serving static files`_
documentation. documentation.
.. _serving static files: http://www.djangoproject.com/documentation/static_files/ .. _serving static files: ../static_files/
Turning off auto-reload Turning off auto-reload
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
@ -253,6 +253,8 @@ files are piped directly into the database after all of the models'
table-creation statements have been executed. Use this SQL hook to populate table-creation statements have been executed. Use this SQL hook to populate
tables with any necessary initial records, SQL functions or test data. tables with any necessary initial records, SQL functions or test data.
Note that the order in which the SQL files are processed is undefined.
sqlreset [appname appname ...] sqlreset [appname appname ...]
-------------------------------------- --------------------------------------
@ -380,7 +382,7 @@ Example usage::
django-admin.py syncdb --verbosity=2 django-admin.py syncdb --verbosity=2
Verbosity determines the amount of notification and debug information that Verbosity determines the amount of notification and debug information that
will be printed to the console. '0' is no output, '1' is normal output, will be printed to the console. '0' is no output, '1' is normal output,
and `2` is verbose output. and `2` is verbose output.

View File

@ -101,9 +101,9 @@ The "From:" header of the e-mail will be the value of the `SERVER_EMAIL setting`
This method exists for convenience and readability. This method exists for convenience and readability.
.. _ADMINS setting: http://www.djangoproject.com/documentation/settings/#admins .. _ADMINS setting: ../settings/#admins
.. _EMAIL_SUBJECT_PREFIX setting: http://www.djangoproject.com/documentation/settings/#email-subject-prefix .. _EMAIL_SUBJECT_PREFIX setting: ../settings/#email-subject-prefix
.. _SERVER_EMAIL setting: http://www.djangoproject.com/documentation/settings/#server-email .. _SERVER_EMAIL setting: ../settings/#server-email
mail_managers() function mail_managers() function
======================== ========================
@ -114,7 +114,7 @@ Here's the definition::
mail_managers(subject, message, fail_silently=False) mail_managers(subject, message, fail_silently=False)
.. _MANAGERS setting: http://www.djangoproject.com/documentation/settings/#managers .. _MANAGERS setting: ../settings/#managers
Examples Examples
======== ========

View File

@ -63,7 +63,7 @@ at any level -- database servers, caching servers or Web/application servers.
The framework cleanly separates components such as its database layer and The framework cleanly separates components such as its database layer and
application layer. And it ships with a simple-yet-powerful `cache framework`_. application layer. And it ships with a simple-yet-powerful `cache framework`_.
.. _`cache framework`: http://www.djangoproject.com/documentation/cache/ .. _`cache framework`: ../cache/
Who's behind this? Who's behind this?
------------------ ------------------
@ -191,7 +191,7 @@ Like we said: We're picky.
We've documented our philosophies on the `design philosophies page`_. We've documented our philosophies on the `design philosophies page`_.
.. _design philosophies page: http://www.djangoproject.com/documentation/design_philosophies/ .. _design philosophies page: ../design_philosophies/
Do you have any of those nifty "screencast" things? Do you have any of those nifty "screencast" things?
--------------------------------------------------- ---------------------------------------------------
@ -277,9 +277,9 @@ How do I get started?
run into trouble. run into trouble.
.. _`Download the code`: http://www.djangoproject.com/download/ .. _`Download the code`: http://www.djangoproject.com/download/
.. _`installation guide`: http://www.djangoproject.com/documentation/install/ .. _`installation guide`: ../install/
.. _tutorial: http://www.djangoproject.com/documentation/tutorial1/ .. _tutorial: ../tutorial1/
.. _documentation: http://www.djangoproject.com/documentation/ .. _documentation: ../
.. _ask questions: http://www.djangoproject.com/community/ .. _ask questions: http://www.djangoproject.com/community/
How do I fix the "install a later version of setuptools" error? How do I fix the "install a later version of setuptools" error?
@ -337,7 +337,7 @@ If you just want to play around and develop things on your local computer, use
the development Web server that comes with Django. Things should Just Work. the development Web server that comes with Django. Things should Just Work.
.. _WSGI: http://www.python.org/peps/pep-0333.html .. _WSGI: http://www.python.org/peps/pep-0333.html
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/ .. _How to use Django with FastCGI: ../fastcgi/
.. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements .. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
How do I install mod_python on Windows? How do I install mod_python on Windows?
@ -464,7 +464,7 @@ Can I use Django with a pre-existing database?
Yes. See `Integrating with a legacy database`_. Yes. See `Integrating with a legacy database`_.
.. _`Integrating with a legacy database`: http://www.djangoproject.com/documentation/legacy_databases/ .. _`Integrating with a legacy database`: ../legacy_databases/
If I make changes to a model, how do I update the database? If I make changes to a model, how do I update the database?
----------------------------------------------------------- -----------------------------------------------------------
@ -511,7 +511,7 @@ type, create an initial data file and put something like this in it::
As explained in the `SQL initial data file`_ documentation, this SQL file can As explained in the `SQL initial data file`_ documentation, this SQL file can
contain arbitrary SQL, so you can make any sorts of changes you need to make. contain arbitrary SQL, so you can make any sorts of changes you need to make.
.. _SQL initial data file: http://www.djangoproject.com/documentation/model_api/#providing-initial-sql-data .. _SQL initial data file: ../model_api/#providing-initial-sql-data
Why is Django leaking memory? Why is Django leaking memory?
----------------------------- -----------------------------
@ -592,7 +592,7 @@ My admin-site CSS and images showed up fine using the development server, but th
See `serving the admin files`_ in the "How to use Django with mod_python" See `serving the admin files`_ in the "How to use Django with mod_python"
documentation. documentation.
.. _serving the admin files: http://www.djangoproject.com/documentation/modpython/#serving-the-admin-files .. _serving the admin files: ../modpython/#serving-the-admin-files
My "list_filter" contains a ManyToManyField, but the filter doesn't display. My "list_filter" contains a ManyToManyField, but the filter doesn't display.
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
@ -630,7 +630,7 @@ site is built using semantic HTML and plenty of CSS hooks, so any changes you'd
like to make should be possible by editing the stylesheet. We've got a like to make should be possible by editing the stylesheet. We've got a
`guide to the CSS used in the admin`_ to get you started. `guide to the CSS used in the admin`_ to get you started.
.. _`guide to the CSS used in the admin`: http://www.djangoproject.com/documentation/admin_css/ .. _`guide to the CSS used in the admin`: ../admin_css/
How do I create users without having to edit password hashes? How do I create users without having to edit password hashes?
------------------------------------------------------------- -------------------------------------------------------------
@ -640,7 +640,7 @@ development version, where this problem was fixed on Aug. 4, 2006.
You can also use the Python API. See `creating users`_ for full info. You can also use the Python API. See `creating users`_ for full info.
.. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users .. _creating users: ../authentication/#creating-users
Contributing code Contributing code
================= =================
@ -651,7 +651,7 @@ How can I get started contributing code to Django?
Thanks for asking! We've written an entire document devoted to this question. Thanks for asking! We've written an entire document devoted to this question.
It's titled `Contributing to Django`_. It's titled `Contributing to Django`_.
.. _Contributing to Django: http://www.djangoproject.com/documentation/contributing/ .. _Contributing to Django: ../contributing/
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch? I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------

View File

@ -17,7 +17,7 @@ served with no startup time. Unlike mod_python (or `mod_perl`_), a FastCGI
process doesn't run inside the Web server process, but in a separate, process doesn't run inside the Web server process, but in a separate,
persistent process. persistent process.
.. _current preferred setup: http://www.djangoproject.com/documentation/modpython/ .. _current preferred setup: ../modpython/
.. _Apache: http://httpd.apache.org/ .. _Apache: http://httpd.apache.org/
.. _mod_python: http://www.modpython.org/ .. _mod_python: http://www.modpython.org/
.. _mod_perl: http://perl.apache.org/ .. _mod_perl: http://perl.apache.org/

View File

@ -29,8 +29,8 @@ To install the flatpages app, follow these steps:
to your MIDDLEWARE_CLASSES_ setting. to your MIDDLEWARE_CLASSES_ setting.
3. Run the command ``manage.py syncdb``. 3. Run the command ``manage.py syncdb``.
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps .. _INSTALLED_APPS: ../settings/#installed-apps
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes .. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
How it works How it works
============ ============
@ -63,9 +63,9 @@ resort.
For more on middleware, read the `middleware docs`_. For more on middleware, read the `middleware docs`_.
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id .. _SITE_ID: ../settings/#site-id
.. _RequestContext: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext .. _RequestContext: ../templates_python/#subclassing-context-djangocontext
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/ .. _middleware docs: ../middleware/
How to add, change and delete flatpages How to add, change and delete flatpages
======================================= =======================================
@ -84,9 +84,9 @@ Flatpages are represented by a standard `Django model`_, which lives in
`django/contrib/flatpages/models.py`_. You can access flatpage objects via the `django/contrib/flatpages/models.py`_. You can access flatpage objects via the
`Django database API`_. `Django database API`_.
.. _Django model: http://www.djangoproject.com/documentation/model_api/ .. _Django model: ../model_api/
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py .. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
.. _Django database API: http://www.djangoproject.com/documentation/db_api/ .. _Django database API: ../db_api/
Flatpage templates Flatpage templates
================== ==================

View File

@ -2,15 +2,27 @@
Forms, fields, and manipulators Forms, fields, and manipulators
=============================== ===============================
Forwards-compatibility note
===========================
The legacy forms/manipulators system described in this document is going to be
replaced in the next Django release. If you're starting from scratch, we
strongly encourage you not to waste your time learning this. Instead, learn and
use the django.newforms system, which we have begun to document in the
`newforms documentation`_.
If you have legacy form/manipulator code, read the "Migration plan" section in
that document to understand how we're making the switch.
.. _newforms documentation: ../newforms/
Introduction
============
Once you've got a chance to play with Django's admin interface, you'll probably Once you've got a chance to play with Django's admin interface, you'll probably
wonder if the fantastic form validation framework it uses is available to user wonder if the fantastic form validation framework it uses is available to user
code. It is, and this document explains how the framework works. code. It is, and this document explains how the framework works.
.. admonition:: A note to the lazy
If all you want to do is present forms for a user to create and/or
update a given object, you may be able to use `generic views`_.
We'll take a top-down approach to examining Django's form validation framework, We'll take a top-down approach to examining Django's form validation framework,
because much of the time you won't need to use the lower-level APIs. Throughout because much of the time you won't need to use the lower-level APIs. Throughout
this document, we'll be working with the following model, a "place" object:: this document, we'll be working with the following model, a "place" object::
@ -41,17 +53,17 @@ this document, we'll be working with the following model, a "place" object::
Defining the above class is enough to create an admin interface to a ``Place``, Defining the above class is enough to create an admin interface to a ``Place``,
but what if you want to allow public users to submit places? but what if you want to allow public users to submit places?
Manipulators Automatic Manipulators
============ ======================
The highest-level interface for object creation and modification is the The highest-level interface for object creation and modification is the
**Manipulator** framework. A manipulator is a utility class tied to a given **automatic Manipulator** framework. An automatic manipulator is a utility
model that "knows" how to create or modify instances of that model and how to class tied to a given model that "knows" how to create or modify instances of
validate data for the object. Manipulators come in two flavors: that model and how to validate data for the object. Automatic Manipulators come
``AddManipulators`` and ``ChangeManipulators``. Functionally they are quite in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally
similar, but the former knows how to create new instances of the model, while they are quite similar, but the former knows how to create new instances of the
the latter modifies existing instances. Both types of classes are automatically model, while the latter modifies existing instances. Both types of classes are
created when you define a new class:: automatically created when you define a new class::
>>> from mysite.myapp.models import Place >>> from mysite.myapp.models import Place
>>> Place.AddManipulator >>> Place.AddManipulator
@ -653,6 +665,6 @@ fails. If no message is passed in, a default message is used.
the executable specified in the ``JING_PATH`` setting (see the settings_ the executable specified in the ``JING_PATH`` setting (see the settings_
document for more details). document for more details).
.. _`generic views`: http://www.djangoproject.com/documentation/generic_views/ .. _`generic views`: ../generic_views/
.. _`models API`: http://www.djangoproject.com/documentation/model_api/ .. _`models API`: ../model_api/
.. _settings: http://www.djangoproject.com/documentation/settings/ .. _settings: ../settings/

View File

@ -71,7 +71,7 @@ are first evaluated, so if you want to pass in a QuerySet via
``extra_context`` that is always fresh you need to wrap it in a function or ``extra_context`` that is always fresh you need to wrap it in a function or
lambda that returns the QuerySet. lambda that returns the QuerySet.
.. _database API docs: http://www.djangoproject.com/documentation/db_api/ .. _database API docs: ../db_api/
"Simple" generic views "Simple" generic views
====================== ======================
@ -99,7 +99,7 @@ which is a dictionary of the parameters captured in the URL.
dictionary is callable, the generic view will call it dictionary is callable, the generic view will call it
just before rendering the template. (**This is new in the just before rendering the template. (**This is new in the
Django development version.**) Django development version.**)
**Example:** **Example:**
Given the following URL patterns:: Given the following URL patterns::
@ -205,11 +205,11 @@ If ``template_name`` isn't specified, this view will use the template
``<app_label>/<model_name>_archive.html`` by default, where: ``<app_label>/<model_name>_archive.html`` by default, where:
* ``<model_name>`` is your model's name in all lowercase. For a model * ``<model_name>`` is your model's name in all lowercase. For a model
``StaffMember``, that'd be ``staffmember``. ``StaffMember``, that'd be ``staffmember``.
* ``<app_label>`` is the right-most part of the full Python path to * ``<app_label>`` is the right-most part of the full Python path to
your model's app. For example, if your model lives in your model's app. For example, if your model lives in
``apps/blog/models.py``, that'd be ``blog``. ``apps/blog/models.py``, that'd be ``blog``.
**Template context:** **Template context:**
@ -223,7 +223,7 @@ In addition to ``extra_context``, the template's context will be:
by ``date_field``. For example, if ``num_latest`` is ``10``, then by ``date_field``. For example, if ``num_latest`` is ``10``, then
``latest`` will be a list of the latest 10 objects in ``queryset``. ``latest`` will be a list of the latest 10 objects in ``queryset``.
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext .. _RequestContext docs: ../templates_python/#subclassing-context-djangocontext
``django.views.generic.date_based.archive_year`` ``django.views.generic.date_based.archive_year``
------------------------------------------------ ------------------------------------------------
@ -266,9 +266,9 @@ to ``True``.
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 * ``template_object_name``: Designates the name of the template variable
to use in the template context. By default, this is ``'object'``. The to use in the template context. By default, this is ``'object'``. The
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.
* ``make_object_list``: A boolean specifying whether to retrieve the full * ``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``, list of objects for this year and pass those to the template. If ``True``,
@ -360,9 +360,9 @@ date in the *future* are not displayed unless you set ``allow_future`` 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 * ``template_object_name``: Designates the name of the template variable
to use in the template context. By default, this is ``'object'``. The to use in the template context. By default, this is ``'object'``. The
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 * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting. to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@ -441,9 +441,9 @@ in the *future* are not displayed unless you set ``allow_future`` to ``True``.
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 * ``template_object_name``: Designates the name of the template variable
to use in the template context. By default, this is ``'object'``. The to use in the template context. By default, this is ``'object'``. The
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 * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting. to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@ -526,9 +526,9 @@ you set ``allow_future`` to ``True``.
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 * ``template_object_name``: Designates the name of the template variable
to use in the template context. By default, this is ``'object'``. The to use in the template context. By default, this is ``'object'``. The
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 * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting. to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@ -638,7 +638,7 @@ future, the view will throw a 404 error by default, unless you set
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 * ``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 * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting. to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@ -710,9 +710,9 @@ A page representing a list of objects.
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 * ``template_object_name``: Designates the name of the template variable
to use in the template context. By default, this is ``'object'``. The to use in the template context. By default, this is ``'object'``. The
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 * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting. to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@ -824,7 +824,7 @@ A page representing an individual object.
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 * ``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 * ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting. to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@ -902,7 +902,7 @@ If ``template_name`` isn't specified, this view will use the template
In addition to ``extra_context``, the template's context will be: In addition to ``extra_context``, the template's context will be:
* ``form``: A ``django.forms.FormWrapper`` instance representing the form * ``form``: A ``django.oldforms.FormWrapper`` instance representing the form
for editing the object. This lets you refer to form fields easily in the for editing the object. This lets you refer to form fields easily in the
template system. template system.
@ -916,8 +916,8 @@ In addition to ``extra_context``, the template's context will be:
See the `manipulator and formfield documentation`_ for more information See the `manipulator and formfield documentation`_ for more information
about using ``FormWrapper`` objects in templates. about using ``FormWrapper`` objects in templates.
.. _authentication system: http://www.djangoproject.com/documentation/authentication/ .. _authentication system: ../authentication/
.. _manipulator and formfield documentation: http://www.djangoproject.com/documentation/forms/ .. _manipulator and formfield documentation: ../forms/
``django.views.generic.create_update.update_object`` ``django.views.generic.create_update.update_object``
---------------------------------------------------- ----------------------------------------------------
@ -973,7 +973,7 @@ object. This uses the automatic manipulators that come with Django models.
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 * ``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'``.
**Template name:** **Template name:**
@ -984,7 +984,7 @@ If ``template_name`` isn't specified, this view will use the template
In addition to ``extra_context``, the template's context will be: In addition to ``extra_context``, the template's context will be:
* ``form``: A ``django.forms.FormWrapper`` instance representing the form * ``form``: A ``django.oldforms.FormWrapper`` instance representing the form
for editing the object. This lets you refer to form fields easily in the for editing the object. This lets you refer to form fields easily in the
template system. template system.
@ -1054,7 +1054,7 @@ contain a form that POSTs to the same URL.
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 * ``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'``.
**Template name:** **Template name:**

View File

@ -53,7 +53,7 @@ internationalization machinery. See the `documentation for USE_I18N`_.
You'll probably also want to remove ``'django.core.context_processors.i18n'`` You'll probably also want to remove ``'django.core.context_processors.i18n'``
from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting. from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
.. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n .. _documentation for USE_I18N: ../settings/#use-i18n
How to specify translation strings How to specify translation strings
================================== ==================================
@ -175,7 +175,7 @@ class, though::
verbose_name = _('my thing') verbose_name = _('my thing')
verbose_name_plural = _('mythings') verbose_name_plural = _('mythings')
.. _Django models: http://www.djangoproject.com/documentation/model_api/ .. _Django models: ../model_api/
Pluralization Pluralization
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -274,7 +274,7 @@ translation string. Example::
In this case, both the tag and the filter will see the already-translated In this case, both the tag and the filter will see the already-translated
string, so they don't need to be aware of translations. string, so they don't need to be aware of translations.
.. _Django templates: http://www.djangoproject.com/documentation/templates_python/ .. _Django templates: ../templates_python/
How to create language files How to create language files
============================ ============================
@ -394,7 +394,7 @@ That's it. Your translations are ready for use.
please let us know! See `Submitting and maintaining translations`_ for please let us know! See `Submitting and maintaining translations`_ for
the steps to take. the steps to take.
.. _Submitting and maintaining translations: http://www.djangoproject.com/documentation/contributing/ .. _Submitting and maintaining translations: ../contributing/
How Django discovers language preference How Django discovers language preference
======================================== ========================================
@ -472,7 +472,7 @@ Notes:
selection to German and English (and any sublanguage, like de-ch or selection to German and English (and any sublanguage, like de-ch or
en-us). en-us).
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages .. _LANGUAGES setting: ../settings/#languages
* If you define a custom ``LANGUAGES`` setting, as explained in the * If you define a custom ``LANGUAGES`` setting, as explained in the
previous bullet, it's OK to mark the languages as translation strings previous bullet, it's OK to mark the languages as translation strings
@ -530,10 +530,10 @@ Note that, with static (middleware-less) translation, the language is in
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's ``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
in ``request.LANGUAGE_CODE``. in ``request.LANGUAGE_CODE``.
.. _settings file: http://www.djangoproject.com/documentation/settings/ .. _settings file: ../settings/
.. _middleware documentation: http://www.djangoproject.com/documentation/middleware/ .. _middleware documentation: ../middleware/
.. _session: http://www.djangoproject.com/documentation/sessions/ .. _session: ../sessions/
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects .. _request object: ../request_response/#httprequest-objects
The ``set_language`` redirect view The ``set_language`` redirect view
================================== ==================================
@ -599,7 +599,7 @@ message file. The choice is yours.
of the settings file to determine this, and a settings file doesn't exist of the settings file to determine this, and a settings file doesn't exist
if you're manually configuring your settings.) if you're manually configuring your settings.)
.. _settings documentation: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable .. _settings documentation: ../settings/#using-settings-without-the-django-settings-module-environment-variable
All message file repositories are structured the same way. They are: All message file repositories are structured the same way. They are:

View File

@ -38,7 +38,7 @@ each platform.
.. _Apache: http://httpd.apache.org/ .. _Apache: http://httpd.apache.org/
.. _mod_python: http://www.modpython.org/ .. _mod_python: http://www.modpython.org/
.. _WSGI: http://www.python.org/peps/pep-0333.html .. _WSGI: http://www.python.org/peps/pep-0333.html
.. _How to use Django with mod_python: http://www.djangoproject.com/documentation/modpython/ .. _How to use Django with mod_python: ../modpython/
.. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements .. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
Get your database running Get your database running
@ -113,14 +113,14 @@ latest bug fixes and improvements, follow these instructions:
svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django
4. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your 3. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts`` system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
(Windows). This step simply lets you type ``django-admin.py`` from within (Windows). This step simply lets you type ``django-admin.py`` from within
any directory, rather than having to qualify the command with the full path any directory, rather than having to qualify the command with the full path
to the file. to the file.
You *don't* have to run ``python setup.py install``, because that command You *don't* have to run ``python setup.py install``, because that command
takes care of steps 3 and 4 for you. takes care of steps 2 and 3 for you.
When you want to update your copy of the Django source code, just run the When you want to update your copy of the Django source code, just run the
command ``svn update`` from within the ``django`` directory. When you do this, command ``svn update`` from within the ``django`` directory. When you do this,

View File

@ -9,7 +9,7 @@ utilities to automate as much of this process as possible.
This document assumes you know the Django basics, as covered in the This document assumes you know the Django basics, as covered in the
`official tutorial`_. `official tutorial`_.
.. _official tutorial: http://www.djangoproject.com/documentation/tutorial1/ .. _official tutorial: ../tutorial1/
Give Django your database parameters Give Django your database parameters
==================================== ====================================
@ -22,18 +22,16 @@ what the name of the database is. Do that by editing these settings in your
* `DATABASE_ENGINE`_ * `DATABASE_ENGINE`_
* `DATABASE_USER`_ * `DATABASE_USER`_
* `DATABASE_PASSWORD`_ * `DATABASE_PASSWORD`_
* `DATABASE_NAME`_
* `DATABASE_HOST`_ * `DATABASE_HOST`_
* `DATABASE_PORT`_ * `DATABASE_PORT`_
.. _settings file: http://www.djangoproject.com/documentation/settings/ .. _settings file: ../settings/
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name .. _DATABASE_NAME: ../settings/#database-name
.. _DATABASE_ENGINE: http://www.djangoproject.com/documentation/settings/#database-engine .. _DATABASE_ENGINE: ../settings/#database-engine
.. _DATABASE_USER: http://www.djangoproject.com/documentation/settings/#database-user .. _DATABASE_USER: ../settings/#database-user
.. _DATABASE_PASSWORD: http://www.djangoproject.com/documentation/settings/#database-password .. _DATABASE_PASSWORD: ../settings/#database-password
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name .. _DATABASE_HOST: ../settings/#database-host
.. _DATABASE_HOST: http://www.djangoproject.com/documentation/settings/#database-host .. _DATABASE_PORT: ../settings/#database-port
.. _DATABASE_PORT: http://www.djangoproject.com/documentation/settings/#database-port
Auto-generate the models Auto-generate the models
======================== ========================
@ -54,7 +52,7 @@ Once you've cleaned up your models, name the file ``models.py`` and put it in
the Python package that holds your app. Then add the app to your the Python package that holds your app. Then add the app to your
``INSTALLED_APPS`` setting. ``INSTALLED_APPS`` setting.
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin.py documentation: ../django_admin/
Install the core Django tables Install the core Django tables
============================== ==============================

View File

@ -47,7 +47,7 @@ Enables site-wide cache. If this is enabled, each Django-powered page will be
cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See
the `cache documentation`_. the `cache documentation`_.
.. _`cache documentation`: http://www.djangoproject.com/documentation/cache/#the-per-site-cache .. _`cache documentation`: ../cache/#the-per-site-cache
django.middleware.common.CommonMiddleware django.middleware.common.CommonMiddleware
----------------------------------------- -----------------------------------------
@ -106,9 +106,10 @@ django.middleware.http.SetRemoteAddrFromForwardedFor
**New in Django development version** **New in Django development version**
Sets ``request['REMOTE_ADDR']`` based on ``request.['HTTP_X_FORWARDED_FOR']``, Sets ``request.META['REMOTE_ADDR']`` based on
if the latter is set. This is useful if you're sitting behind a reverse proxy ``request.META['HTTP_X_FORWARDED_FOR']``, if the latter is set. This is useful
that causes each request's ``REMOTE_ADDR`` to be set to ``127.0.0.1``. if you're sitting behind a reverse proxy that causes each request's
``REMOTE_ADDR`` to be set to ``127.0.0.1``.
**Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're **Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're
not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do
@ -122,7 +123,7 @@ django.contrib.sessions.middleware.SessionMiddleware
Enables session support. See the `session documentation`_. Enables session support. See the `session documentation`_.
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/ .. _`session documentation`: ../sessions/
django.contrib.auth.middleware.AuthenticationMiddleware django.contrib.auth.middleware.AuthenticationMiddleware
------------------------------------------------------- -------------------------------------------------------
@ -130,7 +131,7 @@ django.contrib.auth.middleware.AuthenticationMiddleware
Adds the ``user`` attribute, representing the currently-logged-in user, to Adds the ``user`` attribute, representing the currently-logged-in user, to
every incoming ``HttpRequest`` object. See `Authentication in Web requests`_. every incoming ``HttpRequest`` object. See `Authentication in Web requests`_.
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests .. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
django.middleware.transaction.TransactionMiddleware django.middleware.transaction.TransactionMiddleware
--------------------------------------------------- ---------------------------------------------------
@ -146,7 +147,7 @@ the same transaction control as the view functions.
See the `transaction management documentation`_. See the `transaction management documentation`_.
.. _`transaction management documentation`: http://www.djangoproject.com/documentation/transactions/ .. _`transaction management documentation`: ../transactions/
Writing your own middleware Writing your own middleware
=========================== ===========================

View File

@ -94,7 +94,7 @@ Django places only two restrictions on model field names:
the way Django's query lookup syntax works. For example:: the way Django's query lookup syntax works. For example::
class Example(models.Model): class Example(models.Model):
foo__bar = models.IntegerField() 'foo__bar' has two underscores! foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
These limitations can be worked around, though, because your field name doesn't These limitations can be worked around, though, because your field name doesn't
necessarily have to match your database column name. See `db_column`_ below. necessarily have to match your database column name. See `db_column`_ below.
@ -874,6 +874,10 @@ the relationship should work. All are optional:
force Django to add the descriptor for the reverse force Django to add the descriptor for the reverse
relationship, allowing ``ManyToMany`` relationships to be relationship, allowing ``ManyToMany`` relationships to be
non-symmetrical. non-symmetrical.
``db_table`` The name of the table to create for storing the many-to-many
data. If this is not provided, Django will assume a default
name based upon the names of the two tables being joined.
======================= ============================================================ ======================= ============================================================
@ -1268,6 +1272,24 @@ A few special cases to note about ``list_display``:
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name) return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
colored_name.allow_tags = True colored_name.allow_tags = True
* If the string given is a method of the model that returns True or False
Django will display a pretty "on" or "off" icon if you give the method a
``boolean`` attribute whose value is ``True``.
Here's a full example model::
class Person(models.Model):
first_name = models.CharField(maxlength=50)
birthday = models.DateField()
class Admin:
list_display = ('name', 'born_in_fifties')
def born_in_fifties(self):
return self.birthday.strftime('%Y')[:3] == 5
born_in_fifties.boolean = True
* The ``__str__()`` method is just as valid in ``list_display`` as any * The ``__str__()`` method is just as valid in ``list_display`` as any
other model method, so it's perfectly OK to do this:: other model method, so it's perfectly OK to do this::
@ -1390,7 +1412,10 @@ This should be set to a list of field names that will be searched whenever
somebody submits a search query in that text box. somebody submits a search query in that text box.
These fields should be some kind of text field, such as ``CharField`` or These fields should be some kind of text field, such as ``CharField`` or
``TextField``. ``TextField``. You can also perform a related lookup on a ``ForeignKey`` with
the lookup API "follow" notation::
search_fields = ['foreign_key__related_fieldname']
When somebody does a search in the admin search box, Django splits the search When somebody does a search in the admin search box, Django splits the search
query into words and returns all objects that contain each of the words, case query into words and returns all objects that contain each of the words, case

View File

@ -20,7 +20,7 @@ You may also be interested in `How to use Django with FastCGI`_.
.. _mod_perl: http://perl.apache.org/ .. _mod_perl: http://perl.apache.org/
.. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html .. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html
.. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html .. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/ .. _How to use Django with FastCGI: ../fastcgi/
Basic configuration Basic configuration
=================== ===================

View File

@ -2,9 +2,9 @@
The newforms library The newforms library
==================== ====================
``django.newforms`` is a new replacement for ``django.forms``, the old Django ``django.newforms`` is Django's fantastic new form-handling library. It's a
form/manipulator/validation framework. This document explains how to use this replacement for ``django.forms``, the old form/manipulator/validation
new form library. framework. This document explains how to use this new library.
Migration plan Migration plan
============== ==============
@ -13,18 +13,23 @@ Migration plan
-- i.e., it's not available in the Django 0.95 release. For the next Django -- i.e., it's not available in the Django 0.95 release. For the next Django
release, our plan is to do the following: release, our plan is to do the following:
* Move the current ``django.forms`` to ``django.oldforms``. This will allow * As of revision [4208], we've copied the current ``django.forms`` to
for an eased migration of form code. You'll just have to change your ``django.oldforms``. This allows you to upgrade your code *now* rather
import statements:: than waiting for the backwards-incompatible change and rushing to fix
your code after the fact. Just change your import statements like this::
from django import forms # old from django import forms # old
from django import oldforms as forms # new from django import oldforms as forms # new
* Move the current ``django.newforms`` to ``django.forms``. * At an undecided future date, we will move the current ``django.newforms``
to ``django.forms``. This will be a backwards-incompatible change, and
anybody who is still using the old version of ``django.forms`` at that
time will need to change their import statements, as described in the
previous bullet.
* We will remove ``django.oldforms`` in the release *after* the next Django * We will remove ``django.oldforms`` in the release *after* the next Django
release -- the release that comes after the release in which we're release -- the release that comes after the release in which we're
creating ``django.oldforms``. creating the new ``django.forms``.
With this in mind, we recommend you use the following import statement when With this in mind, we recommend you use the following import statement when
using ``django.newforms``:: using ``django.newforms``::
@ -46,9 +51,14 @@ too messy. The choice is yours.
Overview Overview
======== ========
As the ``django.forms`` system before it, ``django.newforms`` is intended to As with the ``django.forms`` ("manipulators") system before it, ``django.newforms``
handle HTML form display, validation and redisplay. It's what you use if you is intended to handle HTML form display, validation and redisplay. It's what
want to perform server-side validation for an HTML form. you use if you want to perform server-side validation for an HTML form.
For example, if your Web site has a contact form that visitors can use to
send you e-mail, you'd use this library to implement the display of the HTML
form fields, along with the form validation. Any time you need to use an HTML
``<form>``, you can use this library.
The library deals with these concepts: The library deals with these concepts:
@ -62,13 +72,751 @@ The library deals with these concepts:
* **Form** -- A collection of fields that knows how to validate itself and * **Form** -- A collection of fields that knows how to validate itself and
display itself as HTML. display itself as HTML.
The library is decoupled from the other Django components, such as the database
layer, views and templates. It relies only on Django settings, a couple of
``django.utils`` helper functions and Django's internationalization hooks (but
you're not required to be using internationalization features to use this
library).
Form objects
============
Using forms with templates The primary way of using the ``newforms`` library is to create a form object.
========================== Do this by subclassing ``django.newforms.Form`` and specifying the form's
fields, in a declarative style that you'll be familiar with if you've used
Django database models. In this section, we'll iteratively develop a form
object that you might use to implement "contact me" functionality on your
personal Web site.
Using forms in views Start with this basic ``Form`` subclass, which we'll call ``ContactForm``::
====================
from django import newforms as forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField()
A form is composed of ``Field`` objects. In this case, our form has four
fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain
the different types of fields -- e.g., ``CharField`` and ``EmailField`` --
shortly.
Creating ``Form`` instances
---------------------------
A ``Form`` instance is either **bound** or **unbound** to a set of data.
* If it's **bound** to a set of data, it's capable of validating that data
and rendering the form as HTML with the data displayed in the HTML.
* If it's **unbound**, it cannot do validation (because there's no data to
validate!), but it can still render the blank form as HTML.
To create an unbound ``Form`` instance, simply instantiate the class::
>>> f = ContactForm()
To bind data to a form, pass the data as a dictionary as the first parameter to
your ``Form`` class constructor::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
In this dictionary, the keys are the field names, which correspond to the
attributes in your ``Form`` class. The values are the data you're trying
to validate. These will usually be strings, but there's no requirement that
they be strings; the type of data you pass depends on the ``Field``, as we'll
see in a moment.
If you need to distinguish between bound and unbound form instances at runtime,
check the value of the form's ``is_bound`` attribute::
>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({'subject': 'hello'})
>>> f.is_bound
True
Note that passing an empty dictionary creates a *bound* form with empty data::
>>> f = ContactForm({})
>>> f.is_bound
True
If you have a bound ``Form`` instance and want to change the data somehow, or
if you want to bind an unbound ``Form`` instance to some data, create another
``Form`` instance. There is no way to change data in a ``Form`` instance. Once
a ``Form`` instance has been created, you should consider its data immutable,
whether it has data or not.
Using forms to validate data
----------------------------
The primary task of a ``Form`` object is to validate data. With a bound
``Form`` instance, call the ``is_valid()`` method to run validation and return
a boolean designating whether the data was valid::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True
Let's try with some invalid data. In this case, ``subject`` is blank (an error,
because all fields are required by default) and ``sender`` is not a valid
e-mail address::
>>> data = {'subject': '',
... 'message': 'Hi there',
... 'sender': 'invalid e-mail address',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
Access the ``Form`` attribute ``errors`` to get a dictionary of error messages::
>>> f.errors
{'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']}
In this dictionary, the keys are the field names, and the values are lists of
Unicode strings representing the error messages. The error messages are stored
in lists because a field can have multiple error messages.
You can access ``errors`` without having to call ``is_valid()`` first. The
form's data will be validated the first time either you call ``is_valid()`` or
access ``errors``.
Behavior of unbound forms
~~~~~~~~~~~~~~~~~~~~~~~~~
It's meaningless to validate a form with no data, but, for the record, here's
what happens with unbound forms::
>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}
Accessing "clean" data
----------------------
Each ``Field`` in a ``Form`` class is responsible not only for validating data,
but also for "cleaning" it -- normalizing it to a consistent format. This is a
nice feature, because it allows data for a particular field to be input in
a variety of ways, always resulting in consistent output.
For example, ``DateField`` normalizes input into a Python ``datetime.date``
object. Regardless of whether you pass it a string in the format
``'1994-07-15'``, a ``datetime.date`` object or a number of other formats,
``DateField`` will always normalize it to a ``datetime.date`` object as long as
it's valid.
Once you've created a ``Form`` instance with a set of data and validated it,
you can access the clean data via the ``clean_data`` attribute of the ``Form``
object::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.clean_data
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
Note that any text-based field -- such as ``CharField`` or ``EmailField`` --
always cleans the input into a Unicode string. We'll cover the encoding
implications later in this document.
If your data does *not* validate, your ``Form`` instance will not have a
``clean_data`` attribute::
>>> data = {'subject': '',
... 'message': 'Hi there',
... 'sender': 'invalid e-mail address',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.clean_data
Traceback (most recent call last):
...
AttributeError: 'ContactForm' object has no attribute 'clean_data'
``clean_data`` will always *only* contain a key for fields defined in the
``Form``, even if you pass extra data when you define the ``Form``. In this
example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
but ``clean_data`` contains only the form's fields::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True,
... 'extra_field_1': 'foo',
... 'extra_field_2': 'bar',
... 'extra_field_3': 'baz'}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.clean_data # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
Behavior of unbound forms
~~~~~~~~~~~~~~~~~~~~~~~~~
It's meaningless to request "clean" data in a form with no data, but, for the
record, here's what happens with unbound forms::
>>> f = ContactForm()
>>> f.clean_data
Traceback (most recent call last):
...
AttributeError: 'ContactForm' object has no attribute 'clean_data'
Outputting forms as HTML
------------------------
The second task of a ``Form`` object is to render itself as HTML. To do so,
simply ``print`` it::
>>> f = ContactForm()
>>> print f
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
If the form is bound to data, the HTML output will include that data
appropriately. For example, if a field is represented by an
``<input type="text">``, the data will be in the ``value`` attribute. If a
field is represented by an ``<input type="checkbox">``, then that HTML will
include ``checked="checked"`` if appropriate::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> print f
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
This default output is a two-column HTML table, with a ``<tr>`` for each field.
Notice the following:
* For flexibility, the output does *not* include the ``<table>`` and
``</table>`` tags, nor does it include the ``<form>`` and ``</form>``
tags or an ``<input type="submit">`` tag. It's your job to do that.
* Each field type has a default HTML representation. ``CharField`` and
``EmailField`` are represented by an ``<input type="text">``.
``BooleanField`` is represented by an ``<input type="checkbox">``. Note
these are merely sensible defaults; you can specify which HTML to use for
a given field by using widgets, which we'll explain shortly.
* The HTML ``name`` for each tag is taken directly from its attribute name
in the ``ContactForm`` class.
* The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and
``'Cc myself:'`` is generated from the field name by converting all
underscores to spaces and upper-casing the first letter. Again, note
these are merely sensible defaults; you can also specify labels manually.
* Each text label is surrounded in an HTML ``<label>`` tag, which points
to the appropriate form field via its ``id``. Its ``id``, in turn, is
generated by prepending ``'id_'`` to the field name. The ``id``
attributes and ``<label>`` tags are included in the output by default, to
follow best practices, but you can change that behavior.
Although ``<table>`` output is the default output style when you ``print`` a
form, other output styles are available. Each style is available as a method on
a form object, and each rendering method returns a Unicode object.
``as_p()``
~~~~~~~~~~
``Form.as_p()`` renders the form as a series of ``<p>`` tags, with each ``<p>``
containing one field::
>>> f = ContactForm()
>>> f.as_p()
u'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>'
>>> print f.as_p()
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>
<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
``as_ul()``
~~~~~~~~~~~
``Form.as_ul()`` renders the form as a series of ``<li>`` tags, with each
``<li>`` containing one field. It does *not* include the ``<ul>`` or ``</ul>``,
so that you can specify any HTML attributes on the ``<ul>`` for flexibility::
>>> f = ContactForm()
>>> f.as_ul()
u'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>'
>>> print f.as_ul()
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>
<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>
``as_table()``
~~~~~~~~~~~~~~
Finally, ``Form.as_table()`` outputs the form as an HTML ``<table>``. This is
exactly the same as ``print``. In fact, when you ``print`` a form object, it
calls its ``as_table()`` method behind the scenes::
>>> f = ContactForm()
>>> f.as_table()
u'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>'
>>> print f.as_table()
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
Configuring HTML ``<label>`` tags
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An HTML ``<label>`` tag designates which label text is associated with which
form element. This small enhancement makes forms more usable and more accessible
to assistive devices. It's always a good idea to use ``<label>`` tags.
By default, the form rendering methods include HTML ``id`` attributes on the
form elements and corresponding ``<label>`` tags around the labels. The ``id``
attribute values are generated by prepending ``id_`` to the form field names.
This behavior is configurable, though, if you want to change the ``id``
convention or remove HTML ``id`` attributes and ``<label>`` tags entirely.
Use the ``auto_id`` argument to the ``Form`` constructor to control the label
and ``id`` behavior. This argument must be ``True``, ``False`` or a string.
If ``auto_id`` is ``False``, then the form output will not include ``<label>``
tags nor ``id`` attributes::
>>> f = ContactForm(auto_id=False)
>>> print f.as_table()
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
<tr><th>Sender:</th><td><input type="text" name="sender" /></td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print f.as_ul()
<li>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" /></li>
<li>Sender: <input type="text" name="sender" /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print f.as_p()
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" /></p>
<p>Sender: <input type="text" name="sender" /></p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
If ``auto_id`` is set to ``True``, then the form output *will* include
``<label>`` tags and will simply use the field name as its ``id`` for each form
field::
>>> f = ContactForm(auto_id=True)
>>> print f.as_table()
<tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr>
<tr><th><label for="sender">Sender:</label></th><td><input type="text" name="sender" id="sender" /></td></tr>
<tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr>
>>> print f.as_ul()
<li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li>
<li><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></li>
<li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li>
>>> print f.as_p()
<p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p>
<p><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></p>
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p>
If ``auto_id`` is set to a string containing the format character ``'%s'``,
then the form output will include ``<label>`` tags, and will generate ``id``
attributes based on the format string. For example, for a format string
``'field_%s'``, a field named ``subject`` will get the ``id``
``'field_subject'``. Continuing our example::
>>> f = ContactForm(auto_id='id_for_%s')
>>> print f.as_table()
<tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr>
<tr><th><label for="id_for_sender">Sender:</label></th><td><input type="text" name="sender" id="id_for_sender" /></td></tr>
<tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr>
>>> print f.as_ul()
<li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li>
<li><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></li>
<li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
>>> print f.as_p()
<p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p>
<p><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></p>
<p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p>
If ``auto_id`` is set to any other true value -- such as a string that doesn't
include ``%s`` -- then the library will act as if ``auto_id`` is ``True``.
By default, ``auto_id`` is set to the string ``'id_%s'``.
Notes on field ordering
~~~~~~~~~~~~~~~~~~~~~~~
In the ``as_p()``, ``as_ul()`` and ``as_table()`` shortcuts, the fields are
displayed in the order in which you define them in your form class. For
example, in the ``ContactForm`` example, the fields are defined in the order
``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML
output, just change the order in which those fields are listed in the class.
How errors are displayed
~~~~~~~~~~~~~~~~~~~~~~~~
If you render a bound ``Form`` object, the act of rendering will automatically
run the form's validation if it hasn't already happened, and the HTML output
will include the validation errors as a ``<ul>`` near the field. The particular
positioning of the error messages depends on the output method you're using::
>>> data = {'subject': '',
... 'message': 'Hi there',
... 'sender': 'invalid e-mail address',
... 'cc_myself': True}
>>> f = ContactForm(data, auto_id=False)
>>> print f.as_table()
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr>
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul><input type="text" name="sender" value="invalid e-mail address" /></td></tr>
<tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr>
>>> print f.as_ul()
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" value="Hi there" /></li>
<li><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul>Sender: <input type="text" name="sender" value="invalid e-mail address" /></li>
<li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li>
>>> print f.as_p()
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<p><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul></p>
<p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
More granular output
~~~~~~~~~~~~~~~~~~~~
The ``as_p()``, ``as_ul()`` and ``as_table()`` methods are simply shortcuts for
lazy developers -- they're not the only way a form object can be displayed.
To display the HTML for a single field in your form, use dictionary lookup
syntax using the field's name as the key, and print the resulting object::
>>> f = ContactForm()
>>> print f['subject']
<input id="id_subject" type="text" name="subject" maxlength="100" />
>>> print f['message']
<input type="text" name="message" id="id_message" />
>>> print f['sender']
<input type="text" name="sender" id="id_sender" />
>>> print f['cc_myself']
<input type="checkbox" name="cc_myself" id="id_cc_myself" />
Call ``str()`` or ``unicode()`` on the field to get its rendered HTML as a
string or Unicode object, respectively::
>>> str(f['subject'])
'<input id="id_subject" type="text" name="subject" maxlength="100" />'
>>> unicode(f['subject'])
u'<input id="id_subject" type="text" name="subject" maxlength="100" />'
The field-specific output honors the form object's ``auto_id`` setting::
>>> f = ContactForm(auto_id=False)
>>> print f['message']
<input type="text" name="message" />
>>> f = ContactForm(auto_id='id_%s')
>>> print f['message']
<input type="text" name="message" id="id_message" />
For a field's list of errors, access the field's ``errors`` attribute. This
is a list-like object that is displayed as an HTML ``<ul>`` when printed::
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
>>> f = ContactForm(data, auto_id=False)
>>> print f['message']
<input type="text" name="message" />
>>> f['message'].errors
[u'This field is required.']
>>> print f['message'].errors
<ul class="errorlist"><li>This field is required.</li></ul>
>>> f['subject'].errors
[]
>>> print f['subject'].errors
>>> str(f['subject'].errors)
''
Fields
======
When you create a ``Form`` class, the most important part is defining the
fields of the form. Each field has custom validation logic, along with a few
other hooks.
Although the primary way you'll use ``Field`` classes is in ``Form`` classes,
you can also instantiate them and use them directly to get a better idea of
how they work. Each ``Field`` instance has a ``clean()`` method, which takes
a single argument and either raises a ``django.newforms.ValidationError``
exception or returns the clean value::
>>> f = forms.EmailField()
>>> f.clean('foo@example.com')
u'foo@example.com'
>>> f.clean(u'foo@example.com')
u'foo@example.com'
>>> f.clean('invalid e-mail address')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid e-mail address.']
If you've used Django's old forms/validation framework, take care in noticing
this ``ValidationError`` is different than the previous ``ValidationError``.
This one lives at ``django.newforms.ValidationError`` rather than
``django.core.validators.ValidationError``.
Core field arguments
--------------------
Each ``Field`` class constructor takes at least these arguments. Some
``Field`` classes take additional, field-specific arguments, but the following
should *always* be available:
``required``
~~~~~~~~~~~~
By default, each ``Field`` class assumes the value is required, so if you pass
an empty value -- either ``None`` or the empty string (``""``) -- then
``clean()`` will raise a ``ValidationError`` exception::
>>> f = forms.CharField()
>>> f.clean('foo')
u'foo'
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(' ')
u' '
>>> f.clean(0)
u'0'
>>> f.clean(True)
u'True'
>>> f.clean(False)
u'False'
To specify that a field is *not* required, pass ``required=False`` to the
``Field`` constructor::
>>> f = forms.CharField(required=False)
>>> f.clean('foo')
u'foo'
>>> f.clean('')
u''
>>> f.clean(None)
u''
>>> f.clean(0)
u'0'
>>> f.clean(True)
u'True'
>>> f.clean(False)
u'False'
If a ``Field`` has ``required=False`` and you pass ``clean()`` an empty value,
then ``clean()`` will return a *normalized* empty value rather than raising
``ValidationError``. For ``CharField``, this will be a Unicode empty string.
For other ``Field`` classes, it might be ``None``. (This varies from field to
field.)
``label``
~~~~~~~~~
The ``label`` argument lets you specify the "human-friendly" label for this
field. This is used when the ``Field`` is displayed in a ``Form``.
As explained in _`Outputting forms as HTML` above, the default label for a
``Field`` is generated from the field name by converting all underscores to
spaces and upper-casing the first letter. Specify ``label`` if that default
behavior doesn't result in an adequate label.
Here's a full example ``Form`` that implements ``label`` for two of its fields.
We've specified ``auto_id=False`` to simplify the output::
>>> class CommentForm(forms.Form):
... name = forms.CharField(label='Your name')
... url = forms.URLField(label='Your Web site', required=False)
... comment = forms.CharField()
>>> f = CommentForm(auto_id=False)
>>> print f
<tr><th>Your name:</th><td><input type="text" name="name" /></td></tr>
<tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
``initial``
~~~~~~~~~~~
The ``initial`` argument lets you specify the initial value to use when
rendering this ``Field`` in an unbound ``Form``.
The use-case for this is when you want to display an "empty" form in which a
field is initialized to a particular value. For example::
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial='Your name')
... url = forms.URLField(initial='http://')
... comment = forms.CharField()
>>> f = CommentForm(auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
You may be thinking, why not just pass a dictionary of the initial values as
data when displaying the form? Well, if you do that, you'll trigger validation,
and the HTML output will include any validation errors::
>>> class CommentForm(forms.Form):
... name = forms.CharField()
... url = forms.URLField()
... comment = forms.CharField()
>>> default_data = {'name': 'Your name', 'url': 'http://'}
>>> f = CommentForm(default_data, auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
<tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr>
<tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr>
This is why ``initial`` values are only displayed for unbound forms. For bound
forms, the HTML output will use the bound data.
Also note that ``initial`` values are *not* used as "fallback" data in
validation if a particular field's value is not given. ``initial`` values are
*only* intended for initial form display::
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial='Your name')
... url = forms.URLField(initial='http://')
... comment = forms.CharField()
>>> data = {'name': '', 'url': '', 'comment': 'Foo'}
>>> f = CommentForm(data)
>>> f.is_valid()
False
# The form does *not* fall back to using the initial values.
>>> f.errors
{'url': [u'This field is required.'], 'name': [u'This field is required.']}
``widget``
~~~~~~~~~~
The ``widget`` argument lets you specify a ``Widget`` class to use when
rendering this ``Field``. See _`Widgets` below for more information.
``help_text``
~~~~~~~~~~~~~
The ``help_text`` argument lets you specify descriptive text for this
``Field``. If you provide ``help_text``, it will be displayed next to the
``Field`` when the ``Field`` is rendered in a ``Form``.
Here's a full example ``Form`` that implements ``help_text`` for two of its
fields. We've specified ``auto_id=False`` to simplify the output::
>>> class HelpTextContactForm(forms.Form):
... subject = forms.CharField(max_length=100, help_text='100 characters max.')
... message = forms.CharField()
... sender = forms.EmailField(help_text='A valid e-mail address, please.')
... cc_myself = forms.BooleanField()
>>> f = HelpTextContactForm(auto_id=False)
>>> print f.as_table()
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
<tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid e-mail address, please.</td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print f.as_ul()
<li>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</li>
<li>Message: <input type="text" name="message" /></li>
<li>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print f.as_p()
<p>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</p>
<p>Message: <input type="text" name="message" /></p>
<p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
Dynamic initial values
----------------------
The ``initial`` argument to ``Field`` (explained above) lets you hard-code the
initial value for a ``Field`` -- but what if you want to declare the initial
value at runtime? For example, you might want to fill in a ``username`` field
with the username of the current session.
To accomplish this, use the ``initial`` argument to a ``Form``. This argument,
if given, should be a dictionary mapping field names to initial values. Only
include the fields for which you're specifying an initial value; it's not
necessary to include every field in your form. For example::
>>> class CommentForm(forms.Form):
... name = forms.CharField()
... url = forms.URLField()
... comment = forms.CharField()
>>> f = CommentForm(initial={'name': 'your username'}, auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="your username" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
>>> f = CommentForm(initial={'name': 'another username'}, auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="another username" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
Just like the ``initial`` parameter to ``Field``, these values are only
displayed for unbound forms, and they're not used as fallback values if a
particular value isn't provided.
Finally, note that if a ``Field`` defines ``initial`` *and* you include
``initial`` when instantiating the ``Form``, then the latter ``initial`` will
have precedence. In this example, ``initial`` is provided both at the field
level and at the form instance level, and the latter gets precedence::
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial='class')
... url = forms.URLField()
... comment = forms.CharField()
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="instance" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
More coming soon More coming soon
================ ================
@ -77,3 +825,9 @@ That's all the documentation for now. For more, see the file
http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py
-- the unit tests for ``django.newforms``. This can give you a good idea of -- the unit tests for ``django.newforms``. This can give you a good idea of
what's possible. what's possible.
If you're really itching to learn and use this library, please be patient.
We're working hard on finishing both the code and documentation.
Widgets
=======

View File

@ -24,7 +24,7 @@ and Django's ``HttpResponse`` objects are file-like objects.
For more information on the CSV library, see the `CSV library docs`_. For more information on the CSV library, see the `CSV library docs`_.
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/ .. _Request and response objects: ../request_response/
.. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html .. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html
Here's an example:: Here's an example::
@ -115,5 +115,5 @@ a line of CSV for each row. It uses the `addslashes template filter`_ to ensure
there aren't any problems with quotes. If you can be certain your data doesn't there aren't any problems with quotes. If you can be certain your data doesn't
have single or double quotes in it, you can remove the ``addslashes`` filters. have single or double quotes in it, you can remove the ``addslashes`` filters.
.. _Django template system: http://www.djangoproject.com/documentation/templates/ .. _Django template system: ../templates/
.. _addslashes template filter: http://www.djangoproject.com/documentation/templates/#addslashes .. _addslashes template filter: ../templates/#addslashes

View File

@ -43,7 +43,7 @@ objects.
For more information on ``HttpResponse`` objects, see For more information on ``HttpResponse`` objects, see
`Request and response objects`_. `Request and response objects`_.
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/ .. _Request and response objects: ../request_response/
Here's a "Hello World" example:: Here's a "Hello World" example::

View File

@ -11,7 +11,7 @@ understand how Django works, but this isn't intended to be a tutorial or
reference. Please see our more-detailed Django documentation_ when you're ready reference. Please see our more-detailed Django documentation_ when you're ready
to start a project. to start a project.
.. _documentation: http://www.djangoproject.com/documentation/ .. _documentation: ../
Design your model Design your model
================= =================

View File

@ -15,8 +15,8 @@ To install the redirects app, follow these steps:
to your MIDDLEWARE_CLASSES_ setting. to your MIDDLEWARE_CLASSES_ setting.
3. Run the command ``manage.py syncdb``. 3. Run the command ``manage.py syncdb``.
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps .. _INSTALLED_APPS: ../settings/#installed-apps
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes .. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
How it works How it works
============ ============
@ -46,8 +46,8 @@ resort.
For more on middleware, read the `middleware docs`_. For more on middleware, read the `middleware docs`_.
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id .. _SITE_ID: ../settings/#site-id
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/ .. _middleware docs: ../middleware/
How to add, change and delete redirects How to add, change and delete redirects
======================================= =======================================
@ -63,9 +63,9 @@ Via the Python API
------------------ ------------------
Redirects are represented by a standard `Django model`_, which lives in Redirects are represented by a standard `Django model`_, which lives in
`django/contrib/redirects/models/redirects.py`_. You can access redirect `django/contrib/redirects/models.py`_. You can access redirect
objects via the `Django database API`_. objects via the `Django database API`_.
.. _Django model: http://www.djangoproject.com/documentation/model_api/ .. _Django model: ../model_api/
.. _django/contrib/redirects/models/redirects.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models/redirects.py .. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
.. _Django database API: http://www.djangoproject.com/documentation/db_api/ .. _Django database API: ../db_api/

View File

@ -117,14 +117,14 @@ All attributes except ``session`` should be considered read-only.
``AuthenticationMiddleware`` activated. For more, see ``AuthenticationMiddleware`` activated. For more, see
`Authentication in Web requests`_. `Authentication in Web requests`_.
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests .. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
``session`` ``session``
A readable-and-writable, dictionary-like object that represents the current A readable-and-writable, dictionary-like object that represents the current
session. This is only available if your Django installation has session session. This is only available if your Django installation has session
support activated. See the `session documentation`_ for full details. support activated. See the `session documentation`_ for full details.
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/ .. _`session documentation`: ../sessions/
``raw_post_data`` ``raw_post_data``
The raw HTTP POST data. This is only useful for advanced processing. Use The raw HTTP POST data. This is only useful for advanced processing. Use

View File

@ -27,7 +27,7 @@ If you don't want to use sessions, you might as well remove the
``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'`` ``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
from your ``INSTALLED_APPS``. It'll save you a small bit of overhead. from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
.. _middleware: http://www.djangoproject.com/documentation/middleware/ .. _middleware: ../middleware/
Using sessions in views Using sessions in views
======================= =======================
@ -217,6 +217,23 @@ 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 her browser. Use this if you want people to have to log in every time they open
a browser. a browser.
Clearing the session table
==========================
Note that session data can accumulate in the ``django_session`` database table
and Django does *not* provide automatic purging. Therefore, it's your job to
purge expired sessions on a regular basis.
To understand this problem, consider what happens when a user uses a session.
When a user logs in, Django adds a row to the ``django_session`` database
table. Django updates this row each time the session data changes. If the user
logs out manually, Django deletes the row. But if the user does *not* log out,
the row never gets deleted.
Django provides a sample clean-up script in ``django/bin/daily_cleanup.py``.
That script deletes any session in the session table whose ``expire_date`` is
in the past -- but your application may have different requirements.
Settings Settings
======== ========
@ -273,7 +290,7 @@ Whether to save the session data on every request. If this is ``False``
(default), then the session data will only be saved if it has been modified -- (default), then the session data will only be saved if it has been modified --
that is, if any of its dictionary values have been assigned or deleted. that is, if any of its dictionary values have been assigned or deleted.
.. _Django settings: http://www.djangoproject.com/documentation/settings/ .. _Django settings: ../settings/
Technical details Technical details
================= =================

View File

@ -59,7 +59,7 @@ Use the ``--settings`` command-line argument to specify the settings manually::
django-admin.py runserver --settings=mysite.settings django-admin.py runserver --settings=mysite.settings
.. _django-admin.py: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin.py: ../django_admin/
On the server (mod_python) On the server (mod_python)
-------------------------- --------------------------
@ -75,7 +75,7 @@ settings file to use. Do that with ``SetEnv``::
Read the `Django mod_python documentation`_ for more information. Read the `Django mod_python documentation`_ for more information.
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/ .. _Django mod_python documentation: ../modpython/
Default settings Default settings
================ ================
@ -102,7 +102,7 @@ between the current settings file and Django's default settings.
For more, see the `diffsettings documentation`_. For more, see the `diffsettings documentation`_.
.. _diffsettings documentation: http://www.djangoproject.com/documentation/django_admin/#diffsettings .. _diffsettings documentation: ../django_admin/#diffsettings
Using settings in Python code Using settings in Python code
============================= =============================
@ -157,13 +157,13 @@ ABSOLUTE_URL_OVERRIDES
Default: ``{}`` (Empty dictionary) Default: ``{}`` (Empty dictionary)
A dictionary mapping ``"app_label.module_name"`` strings to functions that take A dictionary mapping ``"app_label.model_name"`` strings to functions that take
a model object and return its URL. This is a way of overriding a model object and return its URL. This is a way of overriding
``get_absolute_url()`` methods on a per-installation basis. Example:: ``get_absolute_url()`` methods on a per-installation basis. Example::
ABSOLUTE_URL_OVERRIDES = { ABSOLUTE_URL_OVERRIDES = {
'blogs.blogs': lambda o: "/blogs/%s/" % o.slug, 'blogs.Weblog': lambda o: "/blogs/%s/" % o.slug,
'news.stories': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug), 'news.Story': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
} }
ADMIN_FOR ADMIN_FOR
@ -306,7 +306,7 @@ pages -- and, possibly, by other parts of the system. See
See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT. See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now .. _allowed date format strings: ../templates/#now
DATETIME_FORMAT DATETIME_FORMAT
--------------- ---------------
@ -319,7 +319,7 @@ pages -- and, possibly, by other parts of the system. See
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT. See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now .. _allowed date format strings: ../templates/#now
DEBUG DEBUG
----- -----
@ -433,7 +433,7 @@ A tuple of strings designating all applications that are enabled in this Django
installation. Each string should be a full Python path to a Python package that installation. Each string should be a full Python path to a Python package that
contains a Django application, as created by `django-admin.py startapp`_. contains a Django application, as created by `django-admin.py startapp`_.
.. _django-admin.py startapp: http://www.djangoproject.com/documentation/django_admin/#startapp-appname .. _django-admin.py startapp: ../django_admin/#startapp-appname
INTERNAL_IPS INTERNAL_IPS
------------ ------------
@ -464,7 +464,7 @@ A string representing the language code for this installation. This should be
in standard language format. For example, U.S. English is ``"en-us"``. See the in standard language format. For example, U.S. English is ``"en-us"``. See the
`internationalization docs`_. `internationalization docs`_.
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/ .. _internationalization docs: ../i18n/
LANGUAGES LANGUAGES
--------- ---------
@ -557,6 +557,11 @@ Default: ``''`` (Empty string)
URL that handles the media served from ``MEDIA_ROOT``. URL that handles the media served from ``MEDIA_ROOT``.
Example: ``"http://media.lawrence.com"`` Example: ``"http://media.lawrence.com"``
Note that this should have a trailing slash if it has a path component.
Good: ``"http://www.example.com/static/"``
Bad: ``"http://www.example.com/static"``
MIDDLEWARE_CLASSES MIDDLEWARE_CLASSES
------------------ ------------------
@ -612,7 +617,7 @@ Default: Not defined
A string representing the full Python import path to your root URLconf. For example: A string representing the full Python import path to your root URLconf. For example:
``"mydjangoapps.urls"``. See `How Django processes a request`_. ``"mydjangoapps.urls"``. See `How Django processes a request`_.
.. _How Django processes a request: http://www.djangoproject.com/documentation/url_dispatch/#how-django-processes-a-request .. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request
SECRET_KEY SECRET_KEY
---------- ----------
@ -704,7 +709,7 @@ and a single database can manage content for multiple sites.
See the `site framework docs`_. See the `site framework docs`_.
.. _site framework docs: http://www.djangoproject.com/documentation/sites/ .. _site framework docs: ../sites/
TEMPLATE_CONTEXT_PROCESSORS TEMPLATE_CONTEXT_PROCESSORS
--------------------------- ---------------------------
@ -760,7 +765,7 @@ Default: ``''`` (Empty string)
Output, as a string, that the template system should use for invalid (e.g. Output, as a string, that the template system should use for invalid (e.g.
misspelled) variables. See `How invalid variables are handled`_. misspelled) variables. See `How invalid variables are handled`_.
.. _How invalid variables are handled: http://www.djangoproject.com/documentation/templates_python/#how-invalid-variables-are-handled .. _How invalid variables are handled: ../templates_python/#how-invalid-variables-are-handled
TEST_RUNNER TEST_RUNNER
----------- -----------
@ -798,7 +803,7 @@ pages -- and, possibly, by other parts of the system. See
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and
MONTH_DAY_FORMAT. MONTH_DAY_FORMAT.
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now .. _allowed date format strings: ../templates/#now
TIME_ZONE TIME_ZONE
--------- ---------
@ -868,11 +873,11 @@ Different locales have different formats. For example, U.S. English would say
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT, See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
TIME_FORMAT and MONTH_DAY_FORMAT. TIME_FORMAT and MONTH_DAY_FORMAT.
.. _cache docs: http://www.djangoproject.com/documentation/cache/ .. _cache docs: ../cache/
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/ .. _middleware docs: ../middleware/
.. _session docs: http://www.djangoproject.com/documentation/sessions/ .. _session docs: ../sessions/
.. _See available choices: http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE .. _See available choices: http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
.. _template documentation: http://www.djangoproject.com/documentation/templates_python/ .. _template documentation: ../templates_python/
Creating your own settings Creating your own settings
========================== ==========================

View File

@ -23,8 +23,8 @@ you express this information in Python code.
It works much like Django's `syndication framework`_. To create a sitemap, just It works much like Django's `syndication framework`_. To create a sitemap, just
write a ``Sitemap`` class and point to it in your URLconf_. write a ``Sitemap`` class and point to it in your URLconf_.
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/ .. _syndication framework: ../syndication/
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Installation Installation
============ ============
@ -41,9 +41,9 @@ To install the sitemap app, follow these steps:
reason it needs to go into ``INSTALLED_APPS`` is so that the reason it needs to go into ``INSTALLED_APPS`` is so that the
``load_template_source`` template loader can find the default templates.) ``load_template_source`` template loader can find the default templates.)
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps .. _INSTALLED_APPS: ../settings/#installed-apps
.. _TEMPLATE_LOADERS: http://www.djangoproject.com/documentation/settings/#template-loaders .. _TEMPLATE_LOADERS: ../settings/#template-loaders
.. _sites framework: http://www.djangoproject.com/documentation/sites/ .. _sites framework: ../sites/
Initialization Initialization
============== ==============
@ -68,7 +68,7 @@ The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class ``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
(e.g., ``BlogSitemap(some_var)``). (e.g., ``BlogSitemap(some_var)``).
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Sitemap classes Sitemap classes
=============== ===============
@ -217,8 +217,8 @@ defined for the current ``SITE_ID`` (see the `sites documentation`_) and
creates an entry in the sitemap. These entries include only the ``location`` creates an entry in the sitemap. These entries include only the ``location``
attribute -- not ``lastmod``, ``changefreq`` or ``priority``. attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/ .. _flatpages: ../flatpages/
.. _sites documentation: http://www.djangoproject.com/documentation/sites/ .. _sites documentation: ../sites/
``GenericSitemap`` ``GenericSitemap``
------------------ ------------------
@ -232,7 +232,7 @@ the ``lastmod`` attribute in the generated sitemap. You may also pass
``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap`` ``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
constructor to specify these attributes for all URLs. constructor to specify these attributes for all URLs.
.. _generic views: http://www.djangoproject.com/documentation/generic_views/ .. _generic views: ../generic_views/
Example Example
------- -------
@ -261,7 +261,7 @@ Here's an example of a URLconf_ using both::
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}) (r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
) )
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Creating a sitemap index Creating a sitemap index
======================== ========================

View File

@ -276,8 +276,8 @@ you want your admin site to have access to all objects (not just site-specific
ones), put ``objects = models.Manager()`` in your model, before you define ones), put ``objects = models.Manager()`` in your model, before you define
``CurrentSiteManager``. ``CurrentSiteManager``.
.. _manager: http://www.djangoproject.com/documentation/model_api/#managers .. _manager: ../model_api/#managers
.. _manager documentation: http://www.djangoproject.com/documentation/model_api/#managers .. _manager documentation: ../model_api/#managers
How Django uses the sites framework How Django uses the sites framework
=================================== ===================================
@ -316,7 +316,7 @@ Here's how Django uses the sites framework:
* The shortcut view (``django.views.defaults.shortcut``) uses the domain of * The shortcut view (``django.views.defaults.shortcut``) uses the domain of
the current ``Site`` object when calculating an object's URL. the current ``Site`` object when calculating an object's URL.
.. _redirects framework: http://www.djangoproject.com/documentation/redirects/ .. _redirects framework: ../redirects/
.. _flatpages framework: http://www.djangoproject.com/documentation/flatpages/ .. _flatpages framework: ../flatpages/
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/ .. _syndication framework: ../syndication/
.. _authentication framework: http://www.djangoproject.com/documentation/authentication/ .. _authentication framework: ../authentication/

View File

@ -24,7 +24,7 @@ production setting. Use this only for development.
For information on serving static files in an Apache production environment, For information on serving static files in an Apache production environment,
see the `Django mod_python documentation`_. see the `Django mod_python documentation`_.
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/#serving-media-files .. _Django mod_python documentation: ../modpython/#serving-media-files
How to do it How to do it
============ ============
@ -49,7 +49,7 @@ Examples:
* The file ``/path/bar.jpg`` will not be accessible, because it doesn't * The file ``/path/bar.jpg`` will not be accessible, because it doesn't
fall under the document root. fall under the document root.
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Directory listings Directory listings
================== ==================
@ -122,4 +122,4 @@ associated with the ``django.views.static.serve`` view. If not
Of course, the catch here is that you'll have to remember to set ``DEBUG=False`` Of course, the catch here is that you'll have to remember to set ``DEBUG=False``
in your production settings file. But you should be doing that anyway. in your production settings file. But you should be doing that anyway.
.. _DEBUG setting: http://www.djangoproject.com/documentation/settings/#debug .. _DEBUG setting: ../settings/#debug

View File

@ -26,7 +26,7 @@ to determine which feed to output.
To create a feed, just write a ``Feed`` class and point to it in your URLconf_. To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Initialization Initialization
-------------- --------------
@ -72,8 +72,8 @@ The above example registers two feeds:
Once that's set up, you just need to define the ``Feed`` classes themselves. Once that's set up, you just need to define the ``Feed`` classes themselves.
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
.. _settings file: http://www.djangoproject.com/documentation/settings/ .. _settings file: ../settings/
Feed classes Feed classes
------------ ------------
@ -156,8 +156,8 @@ put into those elements.
{{ obj.description }} {{ obj.description }}
.. _chicagocrime.org: http://www.chicagocrime.org/ .. _chicagocrime.org: http://www.chicagocrime.org/
.. _object-relational mapper: http://www.djangoproject.com/documentation/db_api/ .. _object-relational mapper: ../db_api/
.. _Django templates: http://www.djangoproject.com/documentation/templates/ .. _Django templates: ../templates/
A complex example A complex example
----------------- -----------------
@ -277,7 +277,7 @@ Feeds created by the syndication framework automatically include the
appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
comes directly from your `LANGUAGE_CODE setting`_. comes directly from your `LANGUAGE_CODE setting`_.
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code .. _LANGUAGE_CODE setting: ../settings/#language-code
URLs URLs
---- ----
@ -292,7 +292,7 @@ Atom feeds require a ``<link rel="self">`` that defines the feed's current
location. The syndication framework populates this automatically, using the location. The syndication framework populates this automatically, using the
domain of the current site according to the SITE_ID setting. domain of the current site according to the SITE_ID setting.
.. _SITE_ID setting: http://www.djangoproject.com/documentation/settings/#site-id .. _SITE_ID setting: ../settings/#site-id
Publishing Atom and RSS feeds in tandem Publishing Atom and RSS feeds in tandem
--------------------------------------- ---------------------------------------

View File

@ -792,7 +792,7 @@ Note that if you use ``{% ssi %}``, you'll need to define
See also: ``{% include %}``. See also: ``{% include %}``.
.. _ALLOWED_INCLUDE_ROOTS: http://www.djangoproject.com/documentation/settings/#allowed-include-roots .. _ALLOWED_INCLUDE_ROOTS: ../settings/#allowed-include-roots
templatetag templatetag
~~~~~~~~~~~ ~~~~~~~~~~~
@ -924,13 +924,31 @@ Replaces ampersands with ``&amp;`` entities.
floatformat floatformat
~~~~~~~~~~~ ~~~~~~~~~~~
Rounds a floating-point number to one decimal place -- but only if there's a When used without an argument, rounds a floating-point number to one decimal
decimal part to be displayed. For example: place -- but only if there's a decimal part to be displayed. For example:
* ``36.123`` gets converted to ``36.1`` * ``36.123`` gets converted to ``36.1``
* ``36.15`` gets converted to ``36.2`` * ``36.15`` gets converted to ``36.2``
* ``36`` gets converted to ``36`` * ``36`` gets converted to ``36``
**New in Django development version**
If used with a numeric integer argument, ``floatformat`` rounds a number to that
many decimal places. For example:
* ``36.1234`` with floatformat:3 gets converted to ``36.123``
* ``36`` with floatformat:4 gets converted to ``36.0000``
If the argument passed to ``floatformat`` is negative, it will round a number to
that many decimal places -- but only if there's a decimal part to be displayed.
For example:
* ``36.1234`` with floatformat:-3 gets converted to ``36.123``
* ``36`` with floatformat:-4 gets converted to ``36``
Using ``floatformat`` with no argument is equivalent to using ``floatformat`` with
an argument of ``-1``.
get_digit get_digit
~~~~~~~~~ ~~~~~~~~~
@ -1200,7 +1218,7 @@ django.contrib.humanize
A set of Django template filters useful for adding a "human touch" to data. See A set of Django template filters useful for adding a "human touch" to data. See
the `humanize documentation`_. the `humanize documentation`_.
.. _humanize documentation: http://www.djangoproject.com/documentation/add_ons/#humanize .. _humanize documentation: ../add_ons/#humanize
django.contrib.markup django.contrib.markup
--------------------- ---------------------

View File

@ -11,7 +11,7 @@ If you're looking to use the Django template system as part of another
application -- i.e., without the rest of the framework -- make sure to read application -- i.e., without the rest of the framework -- make sure to read
the `configuration`_ section later in this document. the `configuration`_ section later in this document.
.. _`The Django template language: For template authors`: http://www.djangoproject.com/documentation/templates/ .. _`The Django template language: For template authors`: ../templates/
Basics Basics
====== ======
@ -327,8 +327,8 @@ Note::
Here's what each of the default processors does: Here's what each of the default processors does:
.. _HttpRequest object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects .. _HttpRequest object: ../request_response/#httprequest-objects
.. _TEMPLATE_CONTEXT_PROCESSORS setting: http://www.djangoproject.com/documentation/settings/#template-context-processors .. _TEMPLATE_CONTEXT_PROCESSORS setting: ../settings/#template-context-processors
django.core.context_processors.auth django.core.context_processors.auth
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -353,9 +353,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
permissions that the currently logged-in user has. See the `permissions permissions that the currently logged-in user has. See the `permissions
docs`_. docs`_.
.. _user authentication docs: http://www.djangoproject.com/documentation/authentication/#users .. _user authentication docs: ../authentication/#users
.. _message docs: http://www.djangoproject.com/documentation/authentication/#messages .. _message docs: ../authentication/#messages
.. _permissions docs: http://www.djangoproject.com/documentation/authentication/#permissions .. _permissions docs: ../authentication/#permissions
django.core.context_processors.debug django.core.context_processors.debug
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -383,9 +383,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
See the `internationalization docs`_ for more. See the `internationalization docs`_ for more.
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages .. _LANGUAGES setting: ../settings/#languages
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code .. _LANGUAGE_CODE setting: ../settings/#language-code
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/ .. _internationalization docs: ../i18n/
django.core.context_processors.request django.core.context_processors.request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -944,7 +944,7 @@ The ``takes_context`` parameter defaults to ``False``. When it's set to *True*,
the tag is passed the context object, as in this example. That's the only the tag is passed the context object, as in this example. That's the only
difference between this case and the previous ``inclusion_tag`` example. difference between this case and the previous ``inclusion_tag`` example.
.. _tutorials: http://www.djangoproject.com/documentation/tutorial1/#creating-models .. _tutorials: ../tutorial1/#creating-models
Setting a variable in the context Setting a variable in the context
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1115,5 +1115,5 @@ settings you wish to specify. You might want to consider setting at least
`settings documentation`_, and any setting starting with *TEMPLATE_* `settings documentation`_, and any setting starting with *TEMPLATE_*
is of obvious interest. is of obvious interest.
.. _settings file: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable .. _settings file: ../settings/#using-settings-without-the-django-settings-module-environment-variable
.. _settings documentation: http://www.djangoproject.com/documentation/settings/ .. _settings documentation: ../settings/

View File

@ -13,7 +13,7 @@ changed unexpectedly as a result of the refactor.
Testing a web application is a complex task, as there are many Testing a web application is a complex task, as there are many
components of a web application that must be validated and tested. To components of a web application that must be validated and tested. To
help you test your application, Django provides a test execution help you test your application, Django provides a test execution
framework, and range of utilities that can be used to stimulate and framework, and range of utilities that can be used to simulate and
inspect various facets of a web application. inspect various facets of a web application.
This testing framework is currently under development, and may change This testing framework is currently under development, and may change
@ -220,7 +220,7 @@ can be invoked on the ``Client`` instance.
will result in the evaluation of a GET request equivalent to:: will result in the evaluation of a GET request equivalent to::
http://yoursite.com/customers/details/?name='fred'&age=7 http://yoursite.com/customers/details/?name=fred&age=7
``post(path, data={})`` ``post(path, data={})``
Make a POST request on the provided ``path``. The key-value pairs in the Make a POST request on the provided ``path``. The key-value pairs in the
@ -244,11 +244,11 @@ can be invoked on the ``Client`` instance.
``login(path, username, password)`` ``login(path, username, password)``
In a production site, it is likely that some views will be protected with In a production site, it is likely that some views will be protected with
the @login_required URL provided by ``django.contrib.auth``. Interacting the @login_required decorator provided by ``django.contrib.auth``. Interacting
with a URL that has been login protected is a slightly complex operation, with a URL that has been login protected is a slightly complex operation,
so the Test Client provides a simple URL to automate the login process. A so the Test Client provides a simple method to automate the login process. A
call to ``login()`` stimulates the series of GET and POST calls required call to ``login()`` stimulates the series of GET and POST calls required
to log a user into a @login_required protected URL. to log a user into a @login_required protected view.
If login is possible, the final return value of ``login()`` is the response If login is possible, the final return value of ``login()`` is the response
that is generated by issuing a GET request on the protected URL. If login that is generated by issuing a GET request on the protected URL. If login
@ -415,7 +415,7 @@ arguments:
tested. This is the same format returned by ``django.db.models.get_apps()`` tested. This is the same format returned by ``django.db.models.get_apps()``
Verbosity determines the amount of notification and debug information that Verbosity determines the amount of notification and debug information that
will be printed to the console; '0' is no output, '1' is normal output, will be printed to the console; `0` is no output, `1` is normal output,
and `2` is verbose output. and `2` is verbose output.
Testing utilities Testing utilities

View File

@ -17,7 +17,7 @@ installed by running the Python interactive interpreter and typing
``import django``. If that command runs successfully, with no errors, Django is ``import django``. If that command runs successfully, with no errors, Django is
installed. installed.
.. _`Django installed`: http://www.djangoproject.com/documentation/install/ .. _`Django installed`: ../install/
Creating a project Creating a project
================== ==================
@ -108,7 +108,7 @@ It worked!
Full docs for the development server are at `django-admin documentation`_. Full docs for the development server are at `django-admin documentation`_.
.. _django-admin documentation: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin documentation: ../django_admin/
Database setup Database setup
-------------- --------------
@ -378,7 +378,7 @@ as you like, and it will only ever create the tables that don't exist.
Read the `django-admin.py documentation`_ for full information on what the Read the `django-admin.py documentation`_ for full information on what the
``manage.py`` utility can do. ``manage.py`` utility can do.
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin.py documentation: ../django_admin/
Playing with the API Playing with the API
==================== ====================
@ -555,5 +555,5 @@ For full details on the database API, see our `Database API reference`_.
When you're comfortable with the API, read `part 2 of this tutorial`_ to get When you're comfortable with the API, read `part 2 of this tutorial`_ to get
Django's automatic admin working. Django's automatic admin working.
.. _Database API reference: http://www.djangoproject.com/documentation/db_api/ .. _Database API reference: ../db_api/
.. _part 2 of this tutorial: http://www.djangoproject.com/documentation/tutorial2/ .. _part 2 of this tutorial: ../tutorial2/

View File

@ -5,7 +5,7 @@ Writing your first Django app, part 2
This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll
application and will focus on Django's automatically-generated admin site. application and will focus on Django's automatically-generated admin site.
.. _Tutorial 1: http://www.djangoproject.com/documentation/tutorial1/ .. _Tutorial 1: ../tutorial1/
.. admonition:: Philosophy .. admonition:: Philosophy
@ -64,7 +64,7 @@ tutorial, remember?) You should see the Django admin index page:
By default, you should see two types of editable content: groups and users. By default, you should see two types of editable content: groups and users.
These are core features Django ships with by default. These are core features Django ships with by default.
.. _"I can't log in" questions: http://www.djangoproject.com/documentation/faq/#the-admin-site .. _"I can't log in" questions: ../faq/#the-admin-site
Make the poll app modifiable in the admin Make the poll app modifiable in the admin
========================================= =========================================
@ -402,7 +402,7 @@ Django automatically looks for a ``templates/`` subdirectory within each app
package, for use as a fallback. See the `loader types documentation`_ for full package, for use as a fallback. See the `loader types documentation`_ for full
information. information.
.. _loader types documentation: http://www.djangoproject.com/documentation/templates_python/#loader-types .. _loader types documentation: ../templates_python/#loader-types
Customize the admin index page Customize the admin index page
============================== ==============================
@ -433,5 +433,5 @@ general, see the `Django admin CSS guide`_.
When you're comfortable with the admin site, read `part 3 of this tutorial`_ to When you're comfortable with the admin site, read `part 3 of this tutorial`_ to
start working on public poll views. start working on public poll views.
.. _Django admin CSS guide: http://www.djangoproject.com/documentation/admin_css/ .. _Django admin CSS guide: ../admin_css/
.. _part 3 of this tutorial: http://www.djangoproject.com/documentation/tutorial3/ .. _part 3 of this tutorial: ../tutorial3/

View File

@ -5,7 +5,7 @@ Writing your first Django app, part 3
This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
application and will focus on creating the public interface -- "views." application and will focus on creating the public interface -- "views."
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/ .. _Tutorial 2: ../tutorial2/
Philosophy Philosophy
========== ==========
@ -117,8 +117,8 @@ time the URLconf module is loaded. They're super fast.
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression .. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
.. _Python documentation: http://www.python.org/doc/current/lib/module-re.html .. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
.. _request and response documentation: http://www.djangoproject.com/documentation/request_response/ .. _request and response documentation: ../request_response/
.. _URLconf documentation: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf documentation: ../url_dispatch/
Write your first view Write your first view
===================== =====================
@ -260,8 +260,7 @@ provides a shortcut. Here's the full ``index()`` view, rewritten::
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5] latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list}) return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
Note that we no longer need to import ``loader``, ``Context`` or Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
``HttpResponse``.
The ``render_to_response()`` function takes a template name as its first The ``render_to_response()`` function takes a template name as its first
argument and a dictionary as its optional second argument. It returns an argument and a dictionary as its optional second argument. It returns an
@ -300,7 +299,7 @@ rewritten::
The ``get_object_or_404()`` function takes a Django model module as its first The ``get_object_or_404()`` function takes a Django model module as its first
argument and an arbitrary number of keyword arguments, which it passes to the argument and an arbitrary number of keyword arguments, which it passes to the
module's ``get_object()`` function. It raises ``Http404`` if the object doesn't module's ``get()`` function. It raises ``Http404`` if the object doesn't
exist. exist.
.. admonition:: Philosophy .. admonition:: Philosophy
@ -377,7 +376,7 @@ iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
See the `template guide`_ for full details on how templates work. See the `template guide`_ for full details on how templates work.
.. _template guide: http://www.djangoproject.com/documentation/templates/ .. _template guide: ../templates/
Simplifying the URLconfs Simplifying the URLconfs
======================== ========================
@ -464,4 +463,4 @@ All the poll app cares about is its relative URLs, not its absolute URLs.
When you're comfortable with writing views, read `part 4 of this tutorial`_ to When you're comfortable with writing views, read `part 4 of this tutorial`_ to
learn about simple form processing and generic views. learn about simple form processing and generic views.
.. _part 4 of this tutorial: http://www.djangoproject.com/documentation/tutorial4/ .. _part 4 of this tutorial: ../tutorial4/

Some files were not shown because too many files have changed in this diff Show More