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

boulder-oracle-sprint: Merged to trunk [4210].

git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@4212 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Boulder Sprinters 2006-12-15 18:32:47 +00:00
parent 174463e62c
commit 93d83df611
56 changed files with 3479 additions and 444 deletions

View File

@ -51,6 +51,7 @@ answer newbie questions, and generally made Django that much better:
Jiri Barton Jiri Barton
Ned Batchelder <http://www.nedbatchelder.com/> Ned Batchelder <http://www.nedbatchelder.com/>
Shannon -jj Behrens <http://jjinux.blogspot.com/> Shannon -jj Behrens <http://jjinux.blogspot.com/>
Esdras Beleza <linux@esdrasbeleza.com>
James Bennett James Bennett
Paul Bissex <http://e-scribe.com/> Paul Bissex <http://e-scribe.com/>
Simon Blanchard Simon Blanchard
@ -150,6 +151,7 @@ answer newbie questions, and generally made Django that much better:
SmileyChris <smileychris@gmail.com> SmileyChris <smileychris@gmail.com>
sopel sopel
Thomas Steinacher <tom@eggdrop.ch> Thomas Steinacher <tom@eggdrop.ch>
nowell strite
Radek Švarz <http://www.svarz.cz/translate/> Radek Švarz <http://www.svarz.cz/translate/>
Swaroop C H <http://www.swaroopch.info> Swaroop C H <http://www.swaroopch.info>
Aaron Swartz <http://www.aaronsw.com/> Aaron Swartz <http://www.aaronsw.com/>

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

@ -2,7 +2,7 @@ from django.conf.urls.defaults import *
urlpatterns = patterns('', urlpatterns = patterns('',
# Example: # Example:
# (r'^{{ project_name }}/', include('{{ project_name }}.apps.foo.urls.foo')), # (r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),
# Uncomment this for admin: # Uncomment this for admin:
# (r'^admin/', include('django.contrib.admin.urls')), # (r'^admin/', include('django.contrib.admin.urls')),

View File

@ -44,7 +44,7 @@ var DateTimeShortcuts = {
var shortcuts_span = document.createElement('span'); var shortcuts_span = document.createElement('span');
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling); inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
var now_link = document.createElement('a'); var now_link = document.createElement('a');
now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinute());"); now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());");
now_link.appendChild(document.createTextNode(gettext('Now'))); now_link.appendChild(document.createTextNode(gettext('Now')));
var clock_link = document.createElement('a'); var clock_link = document.createElement('a');
clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');'); clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
@ -80,10 +80,10 @@ var DateTimeShortcuts = {
quickElement('h2', clock_box, gettext('Choose a time')); quickElement('h2', clock_box, gettext('Choose a time'));
time_list = quickElement('ul', clock_box, ''); time_list = quickElement('ul', clock_box, '');
time_list.className = 'timelist'; time_list.className = 'timelist';
quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinute());") quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());")
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '00:00');") quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '00:00:00');")
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '06:00');") quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '06:00:00');")
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '12:00');") quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '12:00:00');")
cancel_p = quickElement('p', clock_box, ''); cancel_p = quickElement('p', clock_box, '');
cancel_p.className = 'calendar-cancel'; cancel_p.className = 'calendar-cancel';

View File

@ -119,6 +119,10 @@ Date.prototype.getTwoDigitMinute = function() {
return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes(); return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes();
} }
Date.prototype.getTwoDigitSecond = function() {
return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds();
}
Date.prototype.getISODate = function() { Date.prototype.getISODate = function() {
return this.getCorrectYear() + '-' + this.getTwoDigitMonth() + '-' + this.getTwoDigitDate(); return this.getCorrectYear() + '-' + this.getTwoDigitMonth() + '-' + this.getTwoDigitDate();
} }
@ -127,6 +131,10 @@ Date.prototype.getHourMinute = function() {
return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute(); return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute();
} }
Date.prototype.getHourMinuteSecond = function() {
return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond();
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// String object extensions // String object extensions
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -7,7 +7,7 @@
<input type="text" size="40" name="{{ search_var }}" value="{{ cl.query|escape }}" id="searchbar" /> <input type="text" size="40" name="{{ search_var }}" value="{{ cl.query|escape }}" id="searchbar" />
<input type="submit" value="{% trans 'Go' %}" /> <input type="submit" value="{% trans 'Go' %}" />
{% if show_result_count %} {% if show_result_count %}
<span class="small quiet">{% blocktrans count cl.result_count as counter %}1 result{% plural %}{{ counter }} results{% endblocktrans %} (<a href="?">{% blocktrans with cl.full_result_count as full_result_count %}{{ full_result_count }} total{% endblocktrans %}</a>)</span> <span class="small quiet">{% blocktrans count cl.result_count as counter %}1 result{% plural %}{{ counter }} results{% endblocktrans %} (<a href="?{% if cl.is_popup %}pop=1{% endif %}">{% blocktrans with cl.full_result_count as full_result_count %}{{ full_result_count }} total{% endblocktrans %}</a>)</span>
{% endif %} {% endif %}
{% for pair in cl.params.items %} {% for pair in cl.params.items %}
{% ifnotequal pair.0 search_var %}<input type="hidden" name="{{ pair.0|escape }}" value="{{ pair.1|escape }}"/>{% endifnotequal %} {% ifnotequal pair.0 search_var %}<input type="hidden" name="{{ pair.0|escape }}" value="{{ pair.1|escape }}"/>{% endifnotequal %}

View File

@ -2,7 +2,7 @@ from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm
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
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
@ -24,7 +24,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,

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
@ -226,7 +226,7 @@ index = staff_member_required(never_cache(index))
def add_stage(request, app_label, model_name, show_delete=False, form_url='', post_url=None, post_url_continue='../%s/', object_id_override=None): def add_stage(request, app_label, model_name, show_delete=False, form_url='', post_url=None, post_url_continue='../%s/', object_id_override=None):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
opts = model._meta opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
@ -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,
@ -302,7 +302,7 @@ def change_stage(request, app_label, model_name, object_id):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
object_id = unquote(object_id) object_id = unquote(object_id)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
opts = model._meta opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
@ -313,8 +313,8 @@ def change_stage(request, app_label, model_name, object_id):
try: try:
manipulator = model.ChangeManipulator(object_id) manipulator = model.ChangeManipulator(object_id)
except ObjectDoesNotExist: except model.DoesNotExist:
raise Http404 raise Http404('%s object with primary key %r does not exist' % (model_name, escape(object_id)))
if request.POST: if request.POST:
new_data = request.POST.copy() new_data = request.POST.copy()
@ -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 = []
@ -490,7 +490,7 @@ def delete_stage(request, app_label, model_name, object_id):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
object_id = unquote(object_id) object_id = unquote(object_id)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
opts = model._meta opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()): if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()):
raise PermissionDenied raise PermissionDenied
@ -527,7 +527,7 @@ def history(request, app_label, model_name, object_id):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
object_id = unquote(object_id) object_id = unquote(object_id)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
action_list = LogEntry.objects.filter(object_id=object_id, action_list = LogEntry.objects.filter(object_id=object_id,
content_type__id__exact=ContentType.objects.get_for_model(model).id).select_related().order_by('action_time') content_type__id__exact=ContentType.objects.get_for_model(model).id).select_related().order_by('action_time')
# If no history was found, see whether this object even exists. # If no history was found, see whether this object even exists.
@ -743,7 +743,7 @@ class ChangeList(object):
def change_list(request, app_label, model_name): def change_list(request, app_label, model_name):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()): if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()):
raise PermissionDenied raise PermissionDenied
try: try:

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):

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

@ -3,9 +3,9 @@ Creates content types for all installed models.
""" """
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.db.models import get_models, signals from django.db.models import get_apps, get_models, signals
def create_contenttypes(app, created_models, verbosity): def create_contenttypes(app, created_models, verbosity=2):
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
app_models = get_models(app) app_models = get_models(app)
if not app_models: if not app_models:
@ -22,4 +22,11 @@ def create_contenttypes(app, created_models, verbosity):
if verbosity >= 2: if verbosity >= 2:
print "Adding content type '%s | %s'" % (ct.app_label, ct.model) print "Adding content type '%s | %s'" % (ct.app_label, ct.model)
def create_all_contenttypes(verbosity=2):
for app in get_apps():
create_contenttypes(app, None, verbosity)
dispatcher.connect(create_contenttypes, signal=signals.post_syncdb) dispatcher.connect(create_contenttypes, signal=signals.post_syncdb)
if __name__ == "__main__":
create_all_contenttypes()

View File

View File

@ -0,0 +1,160 @@
"""
Formtools Preview application.
This is an abstraction of the following workflow:
"Display an HTML form, force a preview, then do something with the submission."
Given a django.newforms.Form object that you define, this takes care of the
following:
* Displays the form as HTML on a Web page.
* Validates the form data once it's submitted via POST.
* If it's valid, displays a preview page.
* If it's not valid, redisplays the form with error messages.
* At the preview page, if the preview confirmation button is pressed, calls
a hook that you define -- a done() method.
The framework enforces the required preview by passing a shared-secret hash to
the preview page. If somebody tweaks the form parameters on the preview page,
the form submission will fail the hash comparison test.
Usage
=====
Subclass FormPreview and define a done() method:
def done(self, request, clean_data):
# ...
This method takes an HttpRequest object and a dictionary of the form data after
it has been validated and cleaned. It should return an HttpResponseRedirect.
Then, just instantiate your FormPreview subclass by passing it a Form class,
and pass that to your URLconf, like so:
(r'^post/$', MyFormPreview(MyForm)),
The FormPreview class has a few other hooks. See the docstrings in the source
code below.
The framework also uses two templates: 'formtools/preview.html' and
'formtools/form.html'. You can override these by setting 'preview_template' and
'form_template' attributes on your FormPreview subclass. See
django/contrib/formtools/templates for the default templates.
"""
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.shortcuts import render_to_response
import cPickle as pickle
import md5
AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter.
class FormPreview(object):
preview_template = 'formtools/preview.html'
form_template = 'formtools/form.html'
# METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
def __init__(self, form):
# form should be a Form class, not an instance.
self.form, self.state = form, {}
def __call__(self, request, *args, **kwargs):
stage = {'1': 'preview', '2': 'post'}.get(request.POST.get(self.unused_name('stage')), 'preview')
self.parse_params(*args, **kwargs)
try:
method = getattr(self, stage + '_' + request.method.lower())
except AttributeError:
raise Http404
return method(request)
def unused_name(self, name):
"""
Given a first-choice name, adds an underscore to the name until it
reaches a name that isn't claimed by any field in the form.
This is calculated rather than being hard-coded so that no field names
are off-limits for use in the form.
"""
while 1:
try:
f = self.form.fields[name]
except KeyError:
break # This field name isn't being used by the form.
name += '_'
return name
def preview_get(self, request):
"Displays the form"
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})
def preview_post(self, request):
"Validates the POST data. If valid, displays the preview page. Else, redisplays form."
f = self.form(request.POST, auto_id=AUTO_ID)
context = {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state}
if f.is_valid():
context['hash_field'] = self.unused_name('hash')
context['hash_value'] = self.security_hash(request, f)
return render_to_response(self.preview_template, context)
else:
return render_to_response(self.form_template, context)
def post_post(self, request):
"Validates the POST data. If valid, calls done(). Else, redisplays form."
f = self.form(request.POST, auto_id=AUTO_ID)
if f.is_valid():
if self.security_hash(request, f) != request.POST.get(self.unused_name('hash')):
return self.failed_hash(request) # Security hash failed.
return self.done(request, f.clean_data)
else:
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state})
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
def parse_params(self, *args, **kwargs):
"""
Given captured args and kwargs from the URLconf, saves something in
self.state and/or raises Http404 if necessary.
For example, this URLconf captures a user_id variable:
(r'^contact/(?P<user_id>\d{1,6})/$', MyFormPreview(MyForm)),
In this case, the kwargs variable in parse_params would be
{'user_id': 32} for a request to '/contact/32/'. You can use that
user_id to make sure it's a valid user and/or save it for later, for
use in done().
"""
pass
def security_hash(self, request, form):
"""
Calculates the security hash for the given Form instance.
This creates a list of the form field names/values in a deterministic
order, pickles the result with the SECRET_KEY setting and takes an md5
hash of that.
Subclasses may want to take into account request-specific information
such as the IP address.
"""
data = [(bf.name, bf.data) for bf in form] + [settings.SECRET_KEY]
# Use HIGHEST_PROTOCOL because it's the most efficient. It requires
# Python 2.3, but Django requires 2.3 anyway, so that's OK.
pickled = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
return md5.new(pickled).hexdigest()
def failed_hash(self, request):
"Returns an HttpResponse in the case of an invalid security hash."
return self.preview_post(request)
# METHODS SUBCLASSES MUST OVERRIDE ########################################
def done(self, request, clean_data):
"Does something with the clean_data and returns an HttpResponseRedirect."
raise NotImplementedError('You must define a done() method on your %s subclass.' % self.__class__.__name__)

View File

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block content %}
{% if form.errors %}<h1>Please correct the following errors</h1>{% else %}<h1>Submit</h1>{% endif %}
<form action="" method="post">
<table>
{{ form }}
</table>
<input type="hidden" name="{{ stage_field }}" value="1" />
<p><input type="submit" value="Submit" /></p>
</form>
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block content %}
<h1>Preview your submission</h1>
<table>
{% for field in form %}
<tr>
<th>{{ field.verbose_name }}:</th>
<td>{{ field.data|escape }}</td>
</tr>
{% endfor %}
</table>
<p>Security hash: {{ hash_value }}</p>
<form action="" method="post">
{% for field in form %}{{ field.as_hidden }}
{% endfor %}
<input type="hidden" name="{{ stage_field }}" value="2" />
<input type="hidden" name="{{ hash_field }}" value="{{ hash_value }}" />
<p><input type="submit" value="Submit" /></p>
</form>
<h1>Or edit it again</h1>
<form action="" method="post">
<table>
{{ form }}
</table>
<input type="hidden" name="{{ stage_field }}" value="1" />
<p><input type="submit" value="Submit changes" /></p>
</form>
{% endblock %}

View File

@ -29,7 +29,7 @@ def ping_google(sitemap_url=None, ping_url=PING_URL):
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
current_site = Site.objects.get_current() current_site = Site.objects.get_current()
url = "%s%s" % (current_site.domain, sitemap) url = "%s%s" % (current_site.domain, sitemap_url)
params = urllib.urlencode({'sitemap':url}) params = urllib.urlencode({'sitemap':url})
urllib.urlopen("%s?%s" % (ping_url, params)) urllib.urlopen("%s?%s" % (ping_url, params))

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.google.com/schemas/sitemap/0.84"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% spaceless %} {% spaceless %}
{% for url in urlset %} {% for url in urlset %}
<url> <url>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.google.com/schemas/sitemap/0.84"> <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% for location in sitemaps %}<sitemap><loc>{{ location|escape }}</loc></sitemap>{% endfor %} {% for location in sitemaps %}<sitemap><loc>{{ location|escape }}</loc></sitemap>{% endfor %}
</sitemapindex> </sitemapindex>

View File

@ -84,7 +84,11 @@ class BaseHandler(object):
# Complain if the view returned None (a common error). # Complain if the view returned None (a common error).
if response is None: if response is None:
raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, callback.func_name) try:
view_name = callback.func_name # If it's a function
except AttributeError:
view_name = callback.__class__.__name__ + '.__call__' # If it's a class
raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)
return response return response
except http.Http404, e: except http.Http404, e:

View File

@ -62,7 +62,7 @@ def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0):
data in the body. data in the body.
""" """
if not size: if not size:
return copyfileobj(fsrc, fdst, length) return
while size > 0: while size > 0:
buf = fsrc.read(min(length, size)) buf = fsrc.read(min(length, size))
if not buf: if not buf:
@ -157,7 +157,11 @@ class WSGIRequest(http.HttpRequest):
return self._raw_post_data return self._raw_post_data
except AttributeError: except AttributeError:
buf = StringIO() buf = StringIO()
content_length = int(self.environ['CONTENT_LENGTH']) try:
# CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)
content_length = int(self.environ.get('CONTENT_LENGTH', 0))
except ValueError: # if CONTENT_LENGTH was empty string or not an integer
content_length = 0
safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length) safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length)
self._raw_post_data = buf.getvalue() self._raw_post_data = buf.getvalue()
buf.close() buf.close()

View File

@ -118,6 +118,8 @@ def runfastcgi(argset=[], **kwargs):
else: else:
return fastcgi_help("ERROR: Implementation must be one of prefork or thread.") return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")
wsgi_opts['debug'] = False # Turn off flup tracebacks
# Prep up and go # Prep up and go
from django.core.handlers.wsgi import WSGIHandler from django.core.handlers.wsgi import WSGIHandler

View File

@ -17,6 +17,6 @@ def populate_xheaders(request, response, model, object_id):
or if the request is from a logged in staff member. or if the request is from a logged in staff member.
""" """
from django.conf import settings from django.conf import settings
if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (request.user.is_authenticated() and request.user.is_staff): if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (hasattr(request, 'user') and request.user.is_authenticated() and request.user.is_staff):
response['X-Object-Type'] = "%s.%s" % (model._meta.app_label, model._meta.object_name.lower()) response['X-Object-Type'] = "%s.%s" % (model._meta.app_label, model._meta.object_name.lower())
response['X-Object-Id'] = str(object_id) response['X-Object-Id'] = str(object_id)

View File

@ -2,7 +2,7 @@ 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.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
@ -210,10 +210,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:
@ -222,7 +222,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.
@ -337,6 +337,12 @@ class Field(object):
return self._choices return self._choices
choices = property(_get_choices) choices = property(_get_choices)
def formfield(self):
"Returns a django.newforms.Field instance for this database Field."
from django.newforms import CharField
# TODO: This is just a temporary default during development.
return CharField(label=capfirst(self.verbose_name))
class AutoField(Field): class AutoField(Field):
empty_strings_allowed = False empty_strings_allowed = False
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -358,7 +364,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
@ -385,11 +391,11 @@ 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]
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):
@ -404,7 +410,7 @@ class CharField(Field):
# 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
@ -472,7 +478,7 @@ 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)
@ -497,7 +503,7 @@ class DateTimeField(DateField):
def get_db_prep_save(self, value): def get_db_prep_save(self, value):
# Casts dates into string format for entry into database. # Casts dates into string format for entry into database.
if isinstance(value, datetime.datetime): if value is not None:
# MySQL/Oracle will throw a warning if microseconds are given, because # MySQL/Oracle will throw a warning if microseconds are given, because
# neither database supports microseconds. # neither database supports microseconds.
if settings.DATABASE_ENGINE in ('mysql', 'oracle') and hasattr(value, 'microsecond'): if settings.DATABASE_ENGINE in ('mysql', 'oracle') and hasattr(value, 'microsecond'):
@ -505,14 +511,6 @@ class DateTimeField(DateField):
# cx_Oracle wants the raw datetime instead of a string. # cx_Oracle wants the raw datetime instead of a string.
if settings.DATABASE_ENGINE != 'oracle': if settings.DATABASE_ENGINE != 'oracle':
value = str(value) value = str(value)
elif isinstance(value, datetime.date):
# MySQL/Oracle will throw a warning if microseconds are given, because
# neither database supports microseconds.
if settings.DATABASE_ENGINE in ('mysql', 'oracle') and hasattr(value, 'microsecond'):
value = datetime.datetime(value.year, value.month, value.day, microsecond=0)
# cx_Oracle wants the raw datetime instead of a string.
if settings.DATABASE_ENGINE != 'oracle':
value = str(value)
return Field.get_db_prep_save(self, value) return Field.get_db_prep_save(self, value)
def get_db_prep_lookup(self, lookup_type, value): def get_db_prep_lookup(self, lookup_type, value):
@ -527,7 +525,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']
@ -564,7 +562,7 @@ 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)
@ -628,7 +626,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]
@ -656,7 +654,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
@ -665,7 +663,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):
@ -673,7 +671,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)
@ -699,7 +697,7 @@ 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]
class IPAddressField(Field): class IPAddressField(Field):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -707,7 +705,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)
@ -718,22 +716,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):
@ -745,15 +743,15 @@ 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]
class TimeField(Field): class TimeField(Field):
empty_strings_allowed = False empty_strings_allowed = False
@ -795,7 +793,7 @@ 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)
@ -808,11 +806,11 @@ class URLField(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 [forms.URLField] return [oldforms.URLField]
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):
@ -823,7 +821,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
@ -836,4 +834,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

@ -5,7 +5,7 @@ from django.db.models.related import RelatedObject
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.dispatch import dispatcher from django.dispatch import dispatcher
# For Python 2.3 # For Python 2.3
@ -493,13 +493,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 +508,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:
@ -581,13 +581,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
@ -622,10 +622,10 @@ 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)

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

@ -208,7 +208,7 @@ class HttpResponse(object):
if path is not None: if path is not None:
self.cookies[key]['path'] = path self.cookies[key]['path'] = path
if domain is not None: if domain is not None:
self.cookies[key]['domain'] = path self.cookies[key]['domain'] = domain
self.cookies[key]['expires'] = 0 self.cookies[key]['expires'] = 0
self.cookies[key]['max-age'] = 0 self.cookies[key]['max-age'] = 0

View File

@ -25,4 +25,5 @@ class GZipMiddleware(object):
response.content = compress_string(response.content) response.content = compress_string(response.content)
response['Content-Encoding'] = 'gzip' response['Content-Encoding'] = 'gzip'
response['Content-Length'] = str(len(response.content))
return response return response

View File

@ -14,15 +14,4 @@ from util import ValidationError
from widgets import * from widgets import *
from fields import * from fields import *
from forms import Form from forms import Form
from models import *
##########################
# DATABASE API SHORTCUTS #
##########################
def form_for_model(model):
"Returns a Form instance for the given Django model class."
raise NotImplementedError
def form_for_fields(field_list):
"Returns a Form instance for the given list of Django database field instances."
raise NotImplementedError

View File

@ -2,8 +2,9 @@
Field classes Field classes
""" """
from util import ValidationError, DEFAULT_ENCODING, smart_unicode from django.utils.translation import gettext
from widgets import TextInput, CheckboxInput, Select, SelectMultiple from util import ValidationError, smart_unicode
from widgets import TextInput, PasswordInput, CheckboxInput, Select, SelectMultiple
import datetime import datetime
import re import re
import time import time
@ -11,6 +12,7 @@ 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', 'MultipleChoiceField',
@ -28,13 +30,26 @@ 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.
def __init__(self, required=True, widget=None): # Tracks each time a Field instance is created. Used to retain order.
self.required = required creation_counter = 0
def __init__(self, required=True, widget=None, label=None):
self.required, self.label = required, label
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.
self.creation_counter = Field.creation_counter
Field.creation_counter += 1
def clean(self, value): def clean(self, value):
""" """
Validates the given value and returns its "cleaned" value as an Validates the given value and returns its "cleaned" value as an
@ -43,13 +58,21 @@ class Field(object):
Raises ValidationError for any errors. Raises ValidationError for any errors.
""" """
if self.required and value in EMPTY_VALUES: if self.required and value in EMPTY_VALUES:
raise ValidationError(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, required=True, widget=None, label=None):
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
Field.__init__(self, required, widget, label)
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."
@ -57,11 +80,15 @@ class CharField(Field):
if value in EMPTY_VALUES: value = u'' if value in EMPTY_VALUES: value = 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(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)
if self.min_length is not None and len(value) < self.min_length: if self.min_length is not None and len(value) < self.min_length:
raise ValidationError(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 clean(self, value): def clean(self, value):
""" """
@ -69,10 +96,12 @@ class IntegerField(Field):
of int(). of int().
""" """
super(IntegerField, self).clean(value) super(IntegerField, self).clean(value)
if not self.required and value in EMPTY_VALUES:
return u''
try: try:
return int(value) return int(value)
except (ValueError, TypeError): except (ValueError, TypeError):
raise ValidationError(u'Enter a whole number.') raise ValidationError(gettext(u'Enter a whole number.'))
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'
@ -83,8 +112,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, required=True, widget=None, label=None):
Field.__init__(self, required, widget) Field.__init__(self, required, widget, label)
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):
@ -104,7 +133,34 @@ class DateField(Field):
return datetime.date(*time.strptime(value, format)[:3]) return datetime.date(*time.strptime(value, format)[:3])
except ValueError: except ValueError:
continue continue
raise ValidationError(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, required=True, widget=None, label=None):
Field.__init__(self, required, widget, label)
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.
"""
Field.clean(self, 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'
@ -119,8 +175,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, required=True, widget=None, label=None):
Field.__init__(self, required, widget) Field.__init__(self, required, widget, label)
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):
@ -140,20 +196,20 @@ class DateTimeField(Field):
return datetime.datetime(*time.strptime(value, format)[:6]) return datetime.datetime(*time.strptime(value, format)[:6])
except ValueError: except ValueError:
continue continue
raise ValidationError(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, error_message=None, required=True, widget=None, label=None):
""" """
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) Field.__init__(self, required, widget, label)
if isinstance(regex, basestring): if isinstance(regex, basestring):
regex = re.compile(regex) regex = re.compile(regex)
self.regex = regex self.regex = regex
self.error_message = error_message or 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):
""" """
@ -163,6 +219,8 @@ class RegexField(Field):
Field.clean(self, value) Field.clean(self, 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'':
return value
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
@ -173,8 +231,8 @@ 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, required=True, widget=None, label=None):
RegexField.__init__(self, email_re, u'Enter a valid e-mail address.', required, widget) RegexField.__init__(self, email_re, gettext(u'Enter a valid e-mail address.'), required, widget, label)
url_re = re.compile( url_re = re.compile(
r'^https?://' # http:// or https:// r'^https?://' # http:// or https://
@ -190,9 +248,9 @@ 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, required=True, verify_exists=False, widget=None, label=None,
validator_user_agent=URL_VALIDATOR_USER_AGENT): validator_user_agent=URL_VALIDATOR_USER_AGENT):
RegexField.__init__(self, url_re, u'Enter a valid URL.', required, widget) RegexField.__init__(self, url_re, gettext(u'Enter a valid URL.'), required, widget, label)
self.verify_exists = verify_exists self.verify_exists = verify_exists
self.user_agent = validator_user_agent self.user_agent = validator_user_agent
@ -212,9 +270,9 @@ class URLField(RegexField):
req = urllib2.Request(value, None, headers) req = urllib2.Request(value, None, headers)
u = urllib2.urlopen(req) u = urllib2.urlopen(req)
except ValueError: except ValueError:
raise ValidationError(u'Enter a valid URL.') raise ValidationError(gettext(u'Enter a valid URL.'))
except: # urllib2.URLError, httplib.InvalidURL, etc. except: # urllib2.URLError, httplib.InvalidURL, etc.
raise ValidationError(u'This URL appears to be a broken link.') raise ValidationError(gettext(u'This URL appears to be a broken link.'))
return value return value
class BooleanField(Field): class BooleanField(Field):
@ -226,10 +284,10 @@ class BooleanField(Field):
return bool(value) return bool(value)
class ChoiceField(Field): class ChoiceField(Field):
def __init__(self, choices=(), required=True, widget=Select): def __init__(self, choices=(), required=True, widget=Select, label=None):
if isinstance(widget, type): if isinstance(widget, type):
widget = widget(choices=choices) widget = widget(choices=choices)
Field.__init__(self, required, widget) Field.__init__(self, required, widget, label)
self.choices = choices self.choices = choices
def clean(self, value): def clean(self, value):
@ -239,37 +297,46 @@ class ChoiceField(Field):
value = Field.clean(self, value) value = Field.clean(self, 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'':
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:
raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % value) raise ValidationError(gettext(u'Select a valid choice. %s is not one of the available choices.') % value)
return value return value
class MultipleChoiceField(ChoiceField): class MultipleChoiceField(ChoiceField):
def __init__(self, choices=(), required=True, widget=SelectMultiple): def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None):
ChoiceField.__init__(self, choices, required, widget) ChoiceField.__init__(self, choices, required, widget, label)
def clean(self, value): def clean(self, value):
""" """
Validates that the input is a list or tuple. Validates that the input is a list or tuple.
""" """
if not isinstance(value, (list, tuple)):
raise ValidationError(u'Enter a list of values.')
if self.required and not value: if self.required and not value:
raise ValidationError(u'This field is required.') raise ValidationError(gettext(u'This field is required.'))
elif not self.required and not value:
return []
if not isinstance(value, (list, tuple)):
raise ValidationError(gettext(u'Enter a list of values.'))
new_value = [] new_value = []
for val in value: for val in value:
val = smart_unicode(val) val = smart_unicode(val)
new_value.append(val) new_value.append(val)
# Validate that each value in the value list is in self.choices. # Validate that each value in the value list is in self.choices.
valid_values = set([k for k, v in self.choices]) valid_values = set([smart_unicode(k) for k, v in self.choices])
for val in new_value: for val in new_value:
if val not in valid_values: if val not in valid_values:
raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % val) raise ValidationError(gettext(u'Select a valid choice. %s is not one of the available choices.') % val)
return new_value return new_value
class ComboField(Field): class ComboField(Field):
def __init__(self, fields=(), required=True, widget=None): def __init__(self, fields=(), required=True, widget=None, label=None):
Field.__init__(self, required, widget) Field.__init__(self, required, widget, label)
# Set 'required' to False on the individual fields, because the
# required validation will be handled by ComboField, not by those
# individual fields.
for f in fields:
f.required = False
self.fields = fields self.fields = fields
def clean(self, value): def clean(self, value):

View File

@ -2,9 +2,11 @@
Form classes Form classes
""" """
from django.utils.datastructures import SortedDict, MultiValueDict
from django.utils.html import escape
from fields import Field from fields import Field
from widgets import TextInput, Textarea from widgets import TextInput, Textarea, HiddenInput
from util import ErrorDict, ErrorList, ValidationError from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError
NON_FIELD_ERRORS = '__all__' NON_FIELD_ERRORS = '__all__'
@ -13,23 +15,37 @@ def pretty_name(name):
name = name[0].upper() + name[1:] name = name[0].upper() + name[1:]
return name.replace('_', ' ') return name.replace('_', ' ')
class SortedDictFromList(SortedDict):
"A dictionary that keeps its keys in the order in which they're inserted."
# This is different than django.utils.datastructures.SortedDict, because
# this takes a list/tuple as the argument to __init__().
def __init__(self, data=None):
if data is None: data = []
self.keyOrder = [d[0] for d in data]
dict.__init__(self, dict(data))
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 'fields'."
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
attrs['fields'] = dict([(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)]) fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)]
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
attrs['fields'] = SortedDictFromList(fields)
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
class Form(object): 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.
def __init__(self, data=None, auto_id='id_%s', prefix=None):
self.ignore_errors = data is None
self.data = data or {} self.data = data or {}
self.auto_id = auto_id self.auto_id = auto_id
self.prefix = prefix
self.clean_data = None # Stores the data after clean() has been called. self.clean_data = None # Stores the data after clean() has been called.
self.__errors = None # Stores the errors after clean() has been called. self.__errors = None # Stores the errors after clean() has been called.
def __str__(self): def __unicode__(self):
return self.as_table() return self.as_table()
def __iter__(self): def __iter__(self):
@ -44,60 +60,75 @@ class Form(object):
raise KeyError('Key %r not found in Form' % name) raise KeyError('Key %r not found in Form' % name)
return BoundField(self, field, name) return BoundField(self, field, name)
def clean(self): def _errors(self):
if self.__errors is None:
self.full_clean()
return self.clean_data
def errors(self):
"Returns an ErrorDict for self.data" "Returns an ErrorDict for self.data"
if self.__errors is None: if self.__errors is None:
self.full_clean() self.full_clean()
return self.__errors return self.__errors
errors = property(_errors)
def is_valid(self): def is_valid(self):
""" """
Returns True if the form has no errors. Otherwise, False. This exists Returns True if the form has no errors. Otherwise, False. If errors are
solely for convenience, so client code can use positive logic rather being ignored, returns False.
than confusing negative logic ("if not form.errors()").
""" """
return not bool(self.errors()) return not self.ignore_errors and not bool(self.errors)
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, errors_on_separate_row):
"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.
output, hidden_fields = [], []
for name, field in self.fields.items():
bf = BoundField(self, field, name)
bf_errors = bf.errors # Cache in local variable.
if bf.is_hidden:
if bf_errors:
top_errors.extend(['(Hidden field %s) %s' % (name, e) for e in bf_errors])
hidden_fields.append(unicode(bf))
else:
if errors_on_separate_row and bf_errors:
output.append(error_row % bf_errors)
output.append(normal_row % {'errors': bf_errors, 'label': bf.label_tag(escape(bf.label+':')), 'field': bf})
if top_errors:
output.insert(0, error_row % top_errors)
if hidden_fields: # Insert any hidden fields in the last row.
str_hidden = u''.join(hidden_fields)
if output:
last_row = output[-1]
# Chop off the trailing row_ender (e.g. '</td></tr>') and insert the hidden fields.
output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
else: # If there aren't any rows in the output, just append the hidden fields.
output.append(str_hidden)
return u'\n'.join(output)
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 u'\n'.join(['<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()]) return self._html_output(u'<tr><th>%(label)s</th><td>%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', True)
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 u'\n'.join(['<li>%s: %s</li>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()]) return self._html_output(u'<li>%(errors)s%(label)s %(field)s</li>', u'<li>%s</li>', '</li>', False)
def as_table_with_errors(self): def as_p(self):
"Returns this form rendered as HTML <tr>s, with errors." "Returns this form rendered as HTML <p>s."
output = [] return self._html_output(u'<p>%(label)s %(field)s</p>', u'<p>%s</p>', '</p>', True)
if self.errors().get(NON_FIELD_ERRORS):
# Errors not corresponding to a particular field are displayed at the top.
output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in self.errors()[NON_FIELD_ERRORS]]))
for name, field in self.fields.items():
bf = BoundField(self, field, name)
if bf.errors:
output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors]))
output.append('<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf))
return u'\n'.join(output)
def as_ul_with_errors(self): def non_field_errors(self):
"Returns this form rendered as HTML <li>s, with errors." """
output = [] Returns an ErrorList of errors that aren't associated with a particular
if self.errors().get(NON_FIELD_ERRORS): field -- i.e., from Form.clean(). Returns an empty ErrorList if there
# Errors not corresponding to a particular field are displayed at the top. are none.
output.append('<li><ul>%s</ul></li>' % '\n'.join(['<li>%s</li>' % e for e in self.errors()[NON_FIELD_ERRORS]])) """
for name, field in self.fields.items(): return self.errors.get(NON_FIELD_ERRORS, ErrorList())
bf = BoundField(self, field, name)
line = '<li>'
if bf.errors:
line += '<ul>%s</ul>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors])
line += '%s: %s</li>' % (pretty_name(name), bf)
output.append(line)
return u'\n'.join(output)
def full_clean(self): def full_clean(self):
""" """
@ -105,8 +136,14 @@ class Form(object):
""" """
self.clean_data = {} self.clean_data = {}
errors = ErrorDict() errors = ErrorDict()
if self.ignore_errors: # Stop further processing.
self.__errors = errors
return
for name, field in self.fields.items(): for name, field in self.fields.items():
value = self.data.get(name, None) # value_from_datadict() gets the data from the dictionary.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
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
@ -126,40 +163,56 @@ class Form(object):
def clean(self): def clean(self):
""" """
Hook for doing any extra form-wide cleaning after Field.clean() been Hook for doing any extra form-wide cleaning after Field.clean() been
called on every field. called on every field. Any ValidationError raised by this method will
not be associated with a particular field; it will have a special-case
association with the field named '__all__'.
""" """
return self.clean_data return self.clean_data
class BoundField(object): 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):
"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)
self.label = self.field.label or pretty_name(name)
def __str__(self): def __unicode__(self):
"Renders this field as an HTML widget." "Renders this field as an HTML widget."
# Use the 'widget' attribute on the field to determine which type # Use the 'widget' attribute on the field to determine which type
# of HTML widget to use. # of HTML widget to use.
return self.as_widget(self._field.widget) value = self.as_widget(self.field.widget)
if not isinstance(value, basestring):
# Some Widget render() methods -- notably RadioSelect -- return a
# "special" object rather than a string. Call the __str__() on that
# object to get its rendered value.
value = value.__str__()
return value
def _errors(self): def _errors(self):
""" """
Returns an ErrorList for this field. Returns an empty ErrorList Returns an ErrorList for this field. Returns an empty ErrorList
if there are none. if there are none.
""" """
try: return self.form.errors.get(self.name, ErrorList())
return self._form.errors()[self._name]
except KeyError:
return ErrorList()
errors = property(_errors) errors = property(_errors)
def as_widget(self, widget, attrs=None): def as_widget(self, widget, attrs=None):
attrs = attrs or {} attrs = attrs or {}
auto_id = self.auto_id auto_id = self.auto_id
if not attrs.has_key('id') and not widget.attrs.has_key('id') and auto_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._form.data.get(self._name, None), attrs=attrs) return widget.render(self.html_name, self.data, attrs=attrs)
def as_text(self, attrs=None): def as_text(self, attrs=None):
""" """
@ -171,15 +224,46 @@ class BoundField(object):
"Returns a string of HTML for representing this as a <textarea>." "Returns a string of HTML for representing this as a <textarea>."
return self.as_widget(Textarea(), attrs) return self.as_widget(Textarea(), attrs)
def as_hidden(self, attrs=None):
"""
Returns a string of HTML for representing this as an <input type="hidden">.
"""
return self.as_widget(HiddenInput(), attrs)
def _data(self):
"Returns the data for this BoundField, or None if it wasn't given."
if self.field.widget.requires_data_list and isinstance(self.form.data, MultiValueDict):
return self.form.data.getlist(self.html_name)
return self.form.data.get(self.html_name, None)
data = property(_data)
def label_tag(self, contents=None):
"""
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
field's HTML-escaped label.
"""
contents = contents or escape(self.label)
widget = self.field.widget
id_ = widget.attrs.get('id') or self.auto_id
if id_:
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents)
return contents
def _is_hidden(self):
"Returns True if this BoundField's widget is hidden."
return self.field.widget.is_hidden
is_hidden = property(_is_hidden)
def _auto_id(self): def _auto_id(self):
""" """
Calculates and returns the ID attribute for this BoundField, if the Calculates and returns the ID attribute for this BoundField, if the
associated Form has specified auto_id. Returns an empty string otherwise. associated Form has specified auto_id. Returns an empty string otherwise.
""" """
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)

19
django/newforms/models.py Normal file
View File

@ -0,0 +1,19 @@
"""
Helper functions for creating Form classes from Django models
and database field objects.
"""
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
__all__ = ('form_for_model', 'form_for_fields')
def form_for_model(model):
"Returns a Form class for the given Django model class."
opts = model._meta
fields = SortedDictFromList([(f.name, f.formfield()) for f in opts.fields + opts.many_to_many])
return type(opts.object_name + 'Form', (BaseForm,), {'fields': fields, '_model_opts': opts})
def form_for_fields(field_list):
"Returns a Form class for the given list of Django database field instances."
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
return type('FormForFields', (BaseForm,), {'fields': fields})

View File

@ -1,13 +1,22 @@
# Default encoding for input byte strings. from django.conf import settings
DEFAULT_ENCODING = 'utf-8' # TODO: First look at django.conf.settings, then fall back to this.
def smart_unicode(s): def smart_unicode(s):
if not isinstance(s, basestring): if not isinstance(s, basestring):
s = unicode(str(s)) s = unicode(str(s))
elif not isinstance(s, unicode): elif not isinstance(s, unicode):
s = unicode(s, DEFAULT_ENCODING) s = unicode(s, settings.DEFAULT_CHARSET)
return s return s
class StrAndUnicode(object):
"""
A class whose __str__ returns its __unicode__ as a bytestring
according to settings.DEFAULT_CHARSET.
Useful as a mix-in.
"""
def __str__(self):
return self.__unicode__().encode(settings.DEFAULT_CHARSET)
class ErrorDict(dict): class ErrorDict(dict):
""" """
A collection of errors that knows how to display itself in various formats. A collection of errors that knows how to display itself in various formats.

View File

@ -5,10 +5,11 @@ HTML Widget classes
__all__ = ( __all__ = (
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput', 'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
'Textarea', 'CheckboxInput', 'Textarea', 'CheckboxInput',
'Select', 'SelectMultiple', 'RadioSelect', 'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
) )
from util import smart_unicode from util import StrAndUnicode, smart_unicode
from django.utils.datastructures import MultiValueDict
from django.utils.html import escape from django.utils.html import escape
from itertools import chain from itertools import chain
@ -23,6 +24,8 @@ flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs
class Widget(object): class Widget(object):
requires_data_list = False # Determines whether render()'s 'value' argument should be a list. 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">.
def __init__(self, attrs=None): def __init__(self, attrs=None):
self.attrs = attrs or {} self.attrs = attrs or {}
@ -30,17 +33,39 @@ class Widget(object):
raise NotImplementedError raise NotImplementedError
def build_attrs(self, extra_attrs=None, **kwargs): def build_attrs(self, extra_attrs=None, **kwargs):
"Helper function for building an attribute dictionary."
attrs = dict(self.attrs, **kwargs) attrs = dict(self.attrs, **kwargs)
if extra_attrs: if extra_attrs:
attrs.update(extra_attrs) attrs.update(extra_attrs)
return attrs return attrs
def value_from_datadict(self, data, name):
"""
Given a dictionary of data and this widget's name, returns the value
of this widget. Returns None if it's not provided.
"""
return data.get(name, None)
def id_for_label(self, id_):
"""
Returns the HTML ID attribute of this Widget for use by a <label>,
given the ID of the field. Returns None if no ID is available.
This hook is necessary because some widgets have multiple HTML
elements and, thus, multiple IDs. In that case, this method should
return an ID value that corresponds to the first ID in the widget's
tags.
"""
return id_
id_for_label = classmethod(id_for_label)
class Input(Widget): class Input(Widget):
""" """
Base class for all <input> widgets (except type='checkbox' and Base class for all <input> widgets (except type='checkbox' and
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)
@ -55,6 +80,7 @@ class PasswordInput(Input):
class HiddenInput(Input): class HiddenInput(Input):
input_type = 'hidden' input_type = 'hidden'
is_hidden = True
class FileInput(Input): class FileInput(Input):
input_type = 'file' input_type = 'file'
@ -67,9 +93,22 @@ class Textarea(Widget):
return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value)) return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
class CheckboxInput(Widget): class CheckboxInput(Widget):
def __init__(self, attrs=None, check_test=bool):
# check_test is a callable that takes a value and returns True
# if the checkbox should be checked for that value.
self.attrs = attrs or {}
self.check_test = check_test
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
final_attrs = self.build_attrs(attrs, type='checkbox', name=name) final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
if value: final_attrs['checked'] = 'checked' try:
result = self.check_test(value)
except: # Silently catch exceptions
result = False
if result:
final_attrs['checked'] = 'checked'
if value not in ('', True, False, None):
final_attrs['value'] = smart_unicode(value) # Only add the 'value' attribute if a value is non-empty.
return u'<input%s />' % flatatt(final_attrs) return u'<input%s />' % flatatt(final_attrs)
class Select(Widget): class Select(Widget):
@ -109,36 +148,44 @@ class SelectMultiple(Widget):
output.append(u'</select>') output.append(u'</select>')
return u'\n'.join(output) return u'\n'.join(output)
class RadioInput(object): def value_from_datadict(self, data, name):
"An object used by RadioFieldRenderer that represents a single <input type='radio'>." if isinstance(data, MultiValueDict):
def __init__(self, name, value, attrs, choice): return data.getlist(name)
self.name, self.value = name, value return data.get(name, None)
self.attrs = attrs or {}
self.choice_value, self.choice_label = choice
def __str__(self): class RadioInput(StrAndUnicode):
"An object used by RadioFieldRenderer that represents a single <input type='radio'>."
def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value
self.attrs = attrs
self.choice_value, self.choice_label = choice
self.index = index
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 == smart_unicode(self.choice_value)
def tag(self): def tag(self):
if self.attrs.has_key('id'):
self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value) final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
if self.is_checked(): if self.is_checked():
final_attrs['checked'] = 'checked' final_attrs['checked'] = 'checked'
return u'<input%s />' % flatatt(final_attrs) return u'<input%s />' % flatatt(final_attrs)
class RadioFieldRenderer(object): class RadioFieldRenderer(StrAndUnicode):
"An object used by RadioSelect to enable customization of radio widgets." "An object used by RadioSelect to enable customization of radio widgets."
def __init__(self, name, value, attrs, choices): def __init__(self, name, value, attrs, choices):
self.name, self.value, self.attrs = name, value, attrs self.name, self.value, self.attrs = name, value, attrs
self.choices = choices self.choices = choices
def __iter__(self): def __iter__(self):
for choice in self.choices: for i, choice in enumerate(self.choices):
yield RadioInput(self.name, self.value, self.attrs, choice) yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
def __str__(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])
@ -147,7 +194,36 @@ class RadioSelect(Select):
"Returns a RadioFieldRenderer instance rather than a Unicode string." "Returns a RadioFieldRenderer instance rather than a Unicode string."
if value is None: value = '' if value is None: value = ''
str_value = smart_unicode(value) # Normalize to string. str_value = smart_unicode(value) # Normalize to string.
attrs = attrs or {}
return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices))) return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
class CheckboxSelectMultiple(Widget): def id_for_label(self, id_):
pass # RadioSelect is represented by multiple <input type="radio"> fields,
# each of which has a distinct ID. The IDs are made distinct by a "_X"
# suffix, where X is the zero-based index of the radio field. Thus,
# the label for a RadioSelect should reference the first one ('_0').
if id_:
id_ += '_0'
return id_
id_for_label = classmethod(id_for_label)
class CheckboxSelectMultiple(SelectMultiple):
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<ul>']
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 option_value, option_label in chain(self.choices, choices):
option_value = smart_unicode(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'</ul>')
return u'\n'.join(output)
def id_for_label(self, id_):
# See the comment for RadioSelect.id_for_label()
if id_:
id_ += '_0'
return id_
id_for_label = classmethod(id_for_label)

1008
django/oldforms/__init__.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -742,7 +742,11 @@ class VariableNode(Node):
def encode_output(self, output): def encode_output(self, output):
# Check type so that we don't run str() on a Unicode object # Check type so that we don't run str() on a Unicode object
if not isinstance(output, basestring): if not isinstance(output, basestring):
try:
return str(output) return str(output)
except UnicodeEncodeError:
# If __str__() returns a Unicode object, convert it to bytestring.
return unicode(output).encode(settings.DEFAULT_CHARSET)
elif isinstance(output, unicode): elif isinstance(output, unicode):
return output.encode(settings.DEFAULT_CHARSET) return output.encode(settings.DEFAULT_CHARSET)
else: else:

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

@ -84,7 +84,7 @@ def object_detail(request, queryset, object_id=None, slug=None,
context_processors=None, template_object_name='object', context_processors=None, template_object_name='object',
mimetype=None): mimetype=None):
""" """
Generic list of objects. Generic detail of an object.
Templates: ``<app_label>/<model_name>_detail.html`` Templates: ``<app_label>/<model_name>_detail.html``
Context: Context:

View File

@ -48,6 +48,23 @@ See the `csrf documentation`_.
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/ .. _csrf documentation: http://www.djangoproject.com/documentation/csrf/
formtools
=========
**New in Django development version**
A set of high-level abstractions for Django forms (django.newforms).
django.contrib.formtools.preview
--------------------------------
An abstraction of the following workflow:
"Display an HTML form, force a preview, then do something with the submission."
Full documentation for this feature does not yet exist, but you can read the
code and docstrings in ``django/contrib/formtools/preview.py`` for a start.
humanize humanize
======== ========

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.
@ -121,6 +126,50 @@ Patch style
it obvious that the ticket includes a patch, and it will add the ticket it obvious that the ticket includes a patch, and it will add the ticket
to the `list of tickets with patches`_. to 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.
Pick an open ticket that is missing some details, and try to replicate the
problem. Fill in the missing pieces of the report. If the ticket doesn't have
a patch, create one.
Once you've completed all the missing details on the ticket and you have a
patch with all the required features, e-mail `django-developers`_. Indicate
that you have triaged a ticket, and recommend a course of action for dealing
with that ticket.
At first, this may require you to be persistent. If you find that your triaged
ticket still isn't getting attention, occasional polite requests for eyeballs
to look at your ticket may be necessary. However, as you earn a reputation for
quality triage work, you should find that it is easier to get the developers'
attention.
.. _required details: `Reporting bugs`_
.. _good patch: `Patch style`_
Submitting and maintaining translations Submitting and maintaining translations
======================================= =======================================
@ -338,21 +387,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
================= =================

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: http://www.djangoproject.com/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

View File

@ -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.
@ -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.

300
docs/newforms.txt Normal file
View File

@ -0,0 +1,300 @@
====================
The newforms library
====================
``django.newforms`` is a new replacement for ``django.forms``, the old Django
form/manipulator/validation framework. This document explains how to use this
new form library.
Migration plan
==============
``django.newforms`` currently is only available in the Django development version
-- i.e., it's not available in the Django 0.95 release. For the next Django
release, our plan is to do the following:
* As of revision [4208], we've copied the current ``django.forms`` to
``django.oldforms``. This allows you to upgrade your code *now* rather
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 oldforms as forms # new
* 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
release -- the release that comes after the release in which we're
creating the new ``django.forms``.
With this in mind, we recommend you use the following import statement when
using ``django.newforms``::
from django import newforms as forms
This way, your code can refer to the ``forms`` module, and when
``django.newforms`` is renamed to ``django.forms``, you'll only have to change
your ``import`` statements.
If you prefer "``import *``" syntax, you can do the following::
from django.newforms import *
This will import all fields, widgets, form classes and other various utilities
into your local namespace. Some people find this convenient; others find it
too messy. The choice is yours.
Overview
========
As the ``django.forms`` ("manipulators") system before it, ``django.newforms``
is intended to handle HTML form display, validation and redisplay. It's what
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:
* **Widget** -- A class that corresponds to an HTML form widget, e.g.
``<input type="text">`` or ``<textarea>``. This handles rendering of the
widget as HTML.
* **Field** -- A class that is responsible for doing validation, e.g.
an ``EmailField`` that makes sure its data is a valid e-mail address.
* **Form** -- A collection of fields that knows how to validate itself and
display itself as HTML.
Form objects
============
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 to implement "contact me" functionality on your personal
Web site.
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.
Outputting forms as HTML
------------------------
The first thing we can do with this is output it as HTML. To do so, instantiate
it and ``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>
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.
More coming soon
================
That's all the documentation for now. For more, see the file
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
what's possible.
Using forms to validate data
----------------------------
Using forms with templates
==========================
Using forms in views
====================

View File

@ -141,7 +141,7 @@ Do this after you've verified that the test cookie worked.
Here's a typical usage example:: Here's a typical usage example::
def login(request): def login(request):
if request.POST: if request.method == 'POST':
if request.session.test_cookie_worked(): if request.session.test_cookie_worked():
request.session.delete_test_cookie() request.session.delete_test_cookie()
return HttpResponse("You're logged in.") return HttpResponse("You're logged in.")

View File

@ -830,7 +830,7 @@ Default: ``Django/<version> (http://www.djangoproject.com/)``
The string to use as the ``User-Agent`` header when checking to see if URLs The string to use as the ``User-Agent`` header when checking to see if URLs
exist (see the ``verify_exists`` option on URLField_). exist (see the ``verify_exists`` option on URLField_).
.. URLField: ../model_api/#urlfield .. _URLField: ../model_api/#urlfield
USE_ETAGS USE_ETAGS
--------- ---------
@ -871,7 +871,7 @@ TIME_FORMAT and MONTH_DAY_FORMAT.
.. _cache docs: http://www.djangoproject.com/documentation/cache/ .. _cache docs: http://www.djangoproject.com/documentation/cache/
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/ .. _middleware docs: http://www.djangoproject.com/documentation/middleware/
.. _session docs: http://www.djangoproject.com/documentation/sessions/ .. _session docs: http://www.djangoproject.com/documentation/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: http://www.djangoproject.com/documentation/templates_python/
Creating your own settings Creating your own settings

View File

@ -5,9 +5,9 @@ The sitemap framework
**New in Django development version**. **New in Django development version**.
Django comes with a high-level sitemap-generating framework that makes Django comes with a high-level sitemap-generating framework that makes
creating `Google Sitemap`_ XML files easy. creating sitemap_ XML files easy.
.. _Google Sitemap: http://www.google.com/webmasters/sitemaps/docs/en/protocol.html .. _sitemap: http://www.sitemaps.org/
Overview Overview
======== ========
@ -55,11 +55,12 @@ URLconf_:
This tells Django to build a sitemap when a client accesses ``/sitemap.xml``. This tells Django to build a sitemap when a client accesses ``/sitemap.xml``.
The name of the sitemap file is not important, but the location is. Google will The name of the sitemap file is not important, but the location is. Search
only index links in your sitemap for the current URL level and below. For engines will only index links in your sitemap for the current URL level and
instance, if ``sitemap.xml`` lives in your root directory, it may reference any below. For instance, if ``sitemap.xml`` lives in your root directory, it may
URL in your site. However, if your sitemap lives at ``/content/sitemap.xml``, reference any URL in your site. However, if your sitemap lives at
it may only reference URLs that begin with ``/content/``. ``/content/sitemap.xml``, it may only reference URLs that begin with
``/content/``.
The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``. The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
``sitemaps`` should be a dictionary that maps a short section label (e.g., ``sitemaps`` should be a dictionary that maps a short section label (e.g.,
@ -199,9 +200,9 @@ If it's an attribute, its value should be either a string or float representing
the priority of *every* object returned by ``items()``. the priority of *every* object returned by ``items()``.
Example values for ``priority``: ``0.4``, ``1.0``. The default priority of a Example values for ``priority``: ``0.4``, ``1.0``. The default priority of a
page is ``0.5``. See Google's documentation for more documentation. page is ``0.5``. See the `sitemaps.org documentation`_ for more.
.. _Google's documentation: http://www.google.com/webmasters/sitemaps/docs/en/protocol.html .. _sitemaps.org documentation: http://www.sitemaps.org/protocol.html#prioritydef
Shortcuts Shortcuts
========= =========

View File

@ -133,7 +133,7 @@ together, picking the test system to match the type of tests you need to
write. write.
For developers new to testing, however, this choice can seem For developers new to testing, however, this choice can seem
confusing, so here are a few key differences to help you decide weather confusing, so here are a few key differences to help you decide whether
doctests or unit tests are right for you. doctests or unit tests are right for you.
If you've been using Python for a while, ``doctest`` will probably feel more If you've been using Python for a while, ``doctest`` will probably feel more

View File

@ -11,19 +11,26 @@ for scheme in INSTALL_SCHEMES.values():
# Compile the list of packages available, because distutils doesn't have # Compile the list of packages available, because distutils doesn't have
# an easy way to do this. # an easy way to do this.
packages, data_files = [], [] packages, data_files = [], []
root_dir = os.path.join(os.path.dirname(__file__), 'django') root_dir = os.path.dirname(__file__)
for dirpath, dirnames, filenames in os.walk(root_dir): len_root_dir = len(root_dir)
django_dir = os.path.join(root_dir, 'django')
for dirpath, dirnames, filenames in os.walk(django_dir):
# Ignore dirnames that start with '.' # Ignore dirnames that start with '.'
for i, dirname in enumerate(dirnames): for i, dirname in enumerate(dirnames):
if dirname.startswith('.'): del dirnames[i] if dirname.startswith('.'): del dirnames[i]
if '__init__.py' in filenames: if '__init__.py' in filenames:
packages.append(dirpath.replace('/', '.')) package = dirpath[len_root_dir:].lstrip('/').replace('/', '.')
packages.append(package)
else: else:
data_files.append((dirpath, [os.path.join(dirpath, f) for f in filenames])) data_files.append((dirpath, [os.path.join(dirpath, f) for f in filenames]))
# Dynamically calculate the version based on django.VERSION.
version = "%d.%d-%s" % (__import__('django').VERSION)
setup( setup(
name = "Django", name = "Django",
version = "0.95", version = version,
url = 'http://www.djangoproject.com/', url = 'http://www.djangoproject.com/',
author = 'Lawrence Journal-World', author = 'Lawrence Journal-World',
author_email = 'holovaty@gmail.com', author_email = 'holovaty@gmail.com',

View File

View File

@ -0,0 +1,44 @@
"""
34. Generating HTML forms from models
Django provides shortcuts for creating Form objects from a model class.
"""
from django.db import models
class Category(models.Model):
name = models.CharField(maxlength=20)
url = models.CharField('The URL', maxlength=20)
def __str__(self):
return self.name
class Article(models.Model):
headline = models.CharField(maxlength=50)
pub_date = models.DateTimeField()
categories = models.ManyToManyField(Category)
def __str__(self):
return self.headline
__test__ = {'API_TESTS': """
>>> from django.newforms import form_for_model
>>> CategoryForm = form_for_model(Category)
>>> f = CategoryForm()
>>> print f
<tr><th><label for="id_id">ID:</label></th><td><input type="text" name="id" id="id_id" /></td></tr>
<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" id="id_name" /></td></tr>
<tr><th><label for="id_url">The URL:</label></th><td><input type="text" name="url" id="id_url" /></td></tr>
>>> print f.as_ul()
<li><label for="id_id">ID:</label> <input type="text" name="id" id="id_id" /></li>
<li><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></li>
<li><label for="id_url">The URL:</label> <input type="text" name="url" id="id_url" /></li>
>>> print f['name']
<input type="text" name="name" id="id_name" />
>>> f = CategoryForm(auto_id=False)
>>> print f.as_ul()
<li>ID: <input type="text" name="id" /></li>
<li>Name: <input type="text" name="name" /></li>
<li>The URL: <input type="text" name="url" /></li>
"""}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from django.conf import settings from django.conf import settings
if __name__ == '__main__': if __name__ == '__main__':
@ -62,6 +63,11 @@ class OtherClass:
def method(self): def method(self):
return "OtherClass.method" return "OtherClass.method"
class UnicodeInStrClass:
"Class whose __str__ returns a Unicode object."
def __str__(self):
return u'ŠĐĆŽćžšđ'
class Templates(unittest.TestCase): class Templates(unittest.TestCase):
def test_templates(self): def test_templates(self):
# NOW and NOW_tz are used by timesince tag tests. # NOW and NOW_tz are used by timesince tag tests.
@ -173,6 +179,10 @@ class Templates(unittest.TestCase):
# Empty strings can be passed as arguments to filters # Empty strings can be passed as arguments to filters
'basic-syntax36': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'), 'basic-syntax36': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'),
# If a variable has a __str__() that returns a Unicode object, the value
# will be converted to a bytestring.
'basic-syntax37': (r'{{ var }}', {'var': UnicodeInStrClass()}, '\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91'),
### COMMENT SYNTAX ######################################################## ### COMMENT SYNTAX ########################################################
'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"), 'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"), 'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),

View File

@ -71,20 +71,24 @@ class InvalidModelTestCase(unittest.TestCase):
def django_tests(verbosity, tests_to_run): def django_tests(verbosity, tests_to_run):
from django.conf import settings from django.conf import settings
from django.db.models.loading import get_apps, load_app
old_installed_apps = settings.INSTALLED_APPS old_installed_apps = settings.INSTALLED_APPS
old_test_database_name = settings.TEST_DATABASE_NAME old_test_database_name = settings.TEST_DATABASE_NAME
old_root_urlconf = settings.ROOT_URLCONF old_root_urlconf = settings.ROOT_URLCONF
old_template_dirs = settings.TEMPLATE_DIRS old_template_dirs = settings.TEMPLATE_DIRS
old_use_i18n = settings.USE_I18N
# Redirect some settings for the duration of these tests # Redirect some settings for the duration of these tests
settings.TEST_DATABASE_NAME = TEST_DATABASE_NAME settings.TEST_DATABASE_NAME = TEST_DATABASE_NAME
settings.INSTALLED_APPS = ALWAYS_INSTALLED_APPS settings.INSTALLED_APPS = ALWAYS_INSTALLED_APPS
settings.ROOT_URLCONF = 'urls' settings.ROOT_URLCONF = 'urls'
settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),) settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),)
settings.USE_I18N = True
# load all the ALWAYS_INSTALLED_APPS # Load all the ALWAYS_INSTALLED_APPS.
# (This import statement is intentionally delayed until after we
# access settings because of the USE_I18N dependency.)
from django.db.models.loading import get_apps, load_app
get_apps() get_apps()
# Load all the test model apps # Load all the test model apps
@ -121,6 +125,7 @@ def django_tests(verbosity, tests_to_run):
settings.TESTS_DATABASE_NAME = old_test_database_name settings.TESTS_DATABASE_NAME = old_test_database_name
settings.ROOT_URLCONF = old_root_urlconf settings.ROOT_URLCONF = old_root_urlconf
settings.TEMPLATE_DIRS = old_template_dirs settings.TEMPLATE_DIRS = old_template_dirs
settings.USE_I18N = old_use_i18n
if __name__ == "__main__": if __name__ == "__main__":
from optparse import OptionParser from optparse import OptionParser