mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +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:
parent
174463e62c
commit
93d83df611
2
AUTHORS
2
AUTHORS
@ -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/>
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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')),
|
||||||
|
@ -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';
|
||||||
|
@ -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
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -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 %}
|
||||||
|
@ -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,
|
||||||
|
@ -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:
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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()
|
||||||
|
0
django/contrib/formtools/__init__.py
Normal file
0
django/contrib/formtools/__init__.py
Normal file
160
django/contrib/formtools/preview.py
Normal file
160
django/contrib/formtools/preview.py
Normal 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__)
|
15
django/contrib/formtools/templates/formtools/form.html
Normal file
15
django/contrib/formtools/templates/formtools/form.html
Normal 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 %}
|
36
django/contrib/formtools/templates/formtools/preview.html
Normal file
36
django/contrib/formtools/templates/formtools/preview.html
Normal 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 %}
|
@ -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))
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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:
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)]
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
||||||
|
@ -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):
|
||||||
|
@ -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
19
django/newforms/models.py
Normal 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})
|
@ -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.
|
||||||
|
@ -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
1008
django/oldforms/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -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):
|
||||||
return str(output)
|
try:
|
||||||
|
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:
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
========
|
========
|
||||||
|
|
||||||
|
@ -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
|
||||||
=================
|
=================
|
||||||
|
@ -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
|
||||||
|
@ -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
300
docs/newforms.txt
Normal 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
|
||||||
|
====================
|
@ -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.")
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
=========
|
=========
|
||||||
|
@ -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
|
||||||
|
15
setup.py
15
setup.py
@ -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',
|
||||||
|
0
tests/modeltests/model_forms/__init__.py
Normal file
0
tests/modeltests/model_forms/__init__.py
Normal file
44
tests/modeltests/model_forms/models.py
Normal file
44
tests/modeltests/model_forms/models.py
Normal 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
@ -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"),
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user