mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +00:00
Merged revisions 4186 to 4454 from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/sqlalchemy@4455 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
122426e745
commit
fe361e678a
6
AUTHORS
6
AUTHORS
@ -78,6 +78,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Clint Ecker
|
||||
Enrico <rico.bl@gmail.com>
|
||||
favo@exoweb.net
|
||||
Eric Floehr <eric@intellovations.com>
|
||||
gandalf@owca.info
|
||||
Baishampayan Ghose
|
||||
martin.glueck@gmail.com
|
||||
@ -118,13 +119,16 @@ answer newbie questions, and generally made Django that much better:
|
||||
Manuzhai
|
||||
Petar Marić
|
||||
mark@junklight.com
|
||||
Yasushi Masuda <whosaysni@gmail.com>
|
||||
mattycakes@gmail.com
|
||||
Jason McBrayer <http://www.carcosa.net/jason/>
|
||||
mccutchen@gmail.com
|
||||
michael.mcewan@gmail.com
|
||||
mitakummaa@gmail.com
|
||||
mmarshall
|
||||
Eric Moritz <http://eric.themoritzfamily.com/>
|
||||
Robin Munn <http://www.geekforgod.com/>
|
||||
Robert Myers <myer0052@gmail.com>
|
||||
Nebojša Dorđević
|
||||
Fraser Nevett <mail@nevett.org>
|
||||
Sam Newman <http://www.magpiebrain.com/>
|
||||
@ -149,6 +153,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
serbaut@gmail.com
|
||||
Pete Shinners <pete@shinners.org>
|
||||
SmileyChris <smileychris@gmail.com>
|
||||
smurf@smurf.noris.de
|
||||
sopel
|
||||
Thomas Steinacher <tom@eggdrop.ch>
|
||||
nowell strite
|
||||
@ -160,6 +165,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Tom Insam
|
||||
Joe Topjian <http://joe.terrarum.net/geek/code/python/django/>
|
||||
Karen Tracey <graybark@bellsouth.net>
|
||||
Makoto Tsuyuki <mtsuyuki@gmail.com>
|
||||
Amit Upadhyay
|
||||
Geert Vanderkelen
|
||||
Milton Waddams
|
||||
|
@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Daily cleanup job.
|
||||
|
||||
|
@ -25,7 +25,7 @@ ADMINS = ()
|
||||
INTERNAL_IPS = ()
|
||||
|
||||
# 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'
|
||||
|
||||
# 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.
|
||||
|
||||
# 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'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
|
@ -0,0 +1,52 @@
|
||||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n admin_modify adminmedia %}
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="../../../../jsi18n/"></script>
|
||||
{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %}
|
||||
{% endblock %}
|
||||
{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
|
||||
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
|
||||
{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
|
||||
{% block breadcrumbs %}{% if not is_popup %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="../../../../">{% trans "Home" %}</a> ›
|
||||
<a href="../../">{{ opts.verbose_name_plural|capfirst|escape }}</a> ›
|
||||
<a href="../">{{ original|truncatewords:"18"|escape }}</a> ›
|
||||
{% trans 'Change password' %}
|
||||
</div>
|
||||
{% endif %}{% endblock %}
|
||||
{% block content %}<div id="content-main">
|
||||
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
|
||||
<div>
|
||||
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
|
||||
{% if form.error_dict %}
|
||||
<p class="errornote">
|
||||
{% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<p>{% blocktrans with original.username|escape as username %}Enter a new password for the user <strong>{{ username }}</strong>.{% endblocktrans %}</p>
|
||||
|
||||
<fieldset class="module aligned">
|
||||
|
||||
<div class="form-row">
|
||||
{{ form.password1.html_error_list }}
|
||||
<label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }}
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
{{ form.password2.html_error_list }}
|
||||
<label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }}
|
||||
<p class="help">{% trans 'Enter the same password as above, for verification.' %}</p>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<div class="submit-row">
|
||||
<input type="submit" value="{% trans 'Change password' %}" class="default" />
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">document.getElementById("{{ first_form_field_id }}").focus();</script>
|
||||
</div>
|
||||
</form></div>
|
||||
{% endblock %}
|
@ -38,7 +38,10 @@
|
||||
<div id="content" class="{% block coltype %}colM{% endblock %}">
|
||||
{% block pretitle %}{% endblock %}
|
||||
{% block content_title %}{% if title %}<h1>{{ title|escape }}</h1>{% endif %}{% endblock %}
|
||||
{% block content %}{{ content }}{% endblock %}
|
||||
{% block content %}
|
||||
{% block object-tools %}{% endblock %}
|
||||
{{ content }}
|
||||
{% endblock %}
|
||||
{% block sidebar %}{% endblock %}
|
||||
<br class="clear" />
|
||||
</div>
|
||||
|
@ -16,11 +16,13 @@
|
||||
</div>
|
||||
{% endif %}{% endblock %}
|
||||
{% block content %}<div id="content-main">
|
||||
{% block object-tools %}
|
||||
{% if change %}{% if not is_popup %}
|
||||
<ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
|
||||
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
||||
</ul>
|
||||
{% endif %}{% endif %}
|
||||
{% endblock %}
|
||||
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
|
||||
<div>
|
||||
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
|
||||
|
@ -7,9 +7,11 @@
|
||||
{% block coltype %}flex{% endblock %}
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
{% block object-tools %}
|
||||
{% if has_add_permission %}
|
||||
<ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
|
||||
{% block search %}{% search_form cl %}{% endblock %}
|
||||
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}
|
||||
|
@ -101,6 +101,10 @@ def result_headers(cl):
|
||||
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
||||
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
|
||||
|
||||
def _boolean_icon(field_val):
|
||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||
return '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
||||
|
||||
def items_for_result(cl, result):
|
||||
first = True
|
||||
pk = cl.lookup_opts.pk.attname
|
||||
@ -114,9 +118,14 @@ def items_for_result(cl, result):
|
||||
try:
|
||||
attr = getattr(result, field_name)
|
||||
allow_tags = getattr(attr, 'allow_tags', False)
|
||||
boolean = getattr(attr, 'boolean', False)
|
||||
if callable(attr):
|
||||
attr = attr()
|
||||
result_repr = str(attr)
|
||||
if boolean:
|
||||
allow_tags = True
|
||||
result_repr = _boolean_icon(attr)
|
||||
else:
|
||||
result_repr = str(attr)
|
||||
except (AttributeError, ObjectDoesNotExist):
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
else:
|
||||
@ -147,8 +156,7 @@ def items_for_result(cl, result):
|
||||
row_class = ' class="nowrap"'
|
||||
# Booleans are special: We use images.
|
||||
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
|
||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
||||
result_repr = _boolean_icon(field_val)
|
||||
# FloatFields are special: Zero-pad the decimals.
|
||||
elif isinstance(f, models.FloatField):
|
||||
if field_val is not None:
|
||||
|
@ -29,6 +29,8 @@ urlpatterns = patterns('',
|
||||
|
||||
# "Add user" -- a special-case view
|
||||
('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
|
||||
# "Change user password" -- another special-case view
|
||||
('^auth/user/(\d+)/password/$', 'django.contrib.admin.views.auth.user_change_password'),
|
||||
|
||||
# Add/change/delete/history
|
||||
('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'),
|
||||
|
@ -1,10 +1,11 @@
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.forms import UserCreationForm, AdminPasswordChangeForm
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django import forms, template
|
||||
from django.shortcuts import render_to_response
|
||||
from django import oldforms, template
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.utils.html import escape
|
||||
|
||||
def user_add_stage(request):
|
||||
if not request.user.has_perm('auth.change_user'):
|
||||
@ -24,7 +25,7 @@ def user_add_stage(request):
|
||||
return HttpResponseRedirect('../%s/' % new_user.id)
|
||||
else:
|
||||
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', {
|
||||
'title': _('Add user'),
|
||||
'form': form,
|
||||
@ -42,3 +43,35 @@ def user_add_stage(request):
|
||||
'username_help_text': User._meta.get_field('username').help_text,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
user_add_stage = staff_member_required(user_add_stage)
|
||||
|
||||
def user_change_password(request, id):
|
||||
if not request.user.has_perm('auth.change_user'):
|
||||
raise PermissionDenied
|
||||
user = get_object_or_404(User, pk=id)
|
||||
manipulator = AdminPasswordChangeForm(user)
|
||||
if request.method == 'POST':
|
||||
new_data = request.POST.copy()
|
||||
errors = manipulator.get_validation_errors(new_data)
|
||||
if not errors:
|
||||
new_user = manipulator.save(new_data)
|
||||
msg = _('Password changed successfully.')
|
||||
request.user.message_set.create(message=msg)
|
||||
return HttpResponseRedirect('..')
|
||||
else:
|
||||
errors = new_data = {}
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
return render_to_response('admin/auth/user/change_password.html', {
|
||||
'title': _('Change password: %s') % escape(user.username),
|
||||
'form': form,
|
||||
'is_popup': request.REQUEST.has_key('_popup'),
|
||||
'add': True,
|
||||
'change': False,
|
||||
'has_delete_permission': False,
|
||||
'has_change_permission': True,
|
||||
'has_absolute_url': False,
|
||||
'first_form_field_id': 'id_password1',
|
||||
'opts': User._meta,
|
||||
'original': user,
|
||||
'show_save': True,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
user_change_password = staff_member_required(user_change_password)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django import forms, template
|
||||
from django import oldforms, template
|
||||
from django.conf import settings
|
||||
from django.contrib.admin.filterspecs import FilterSpec
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
@ -46,8 +46,8 @@ def quote(s):
|
||||
"""
|
||||
Ensure that primary key values do not confuse the admin URLs by escaping
|
||||
any '/', '_' and ':' characters. Similar to urllib.quote, except that the
|
||||
quoting is slightly different so that it doesn't get autoamtically
|
||||
unquoted by the web browser.
|
||||
quoting is slightly different so that it doesn't get automatically
|
||||
unquoted by the Web browser.
|
||||
"""
|
||||
if type(s) != type(''):
|
||||
return s
|
||||
@ -283,7 +283,7 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po
|
||||
errors = {}
|
||||
|
||||
# Populate the FormWrapper.
|
||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
|
||||
c = template.RequestContext(request, {
|
||||
'title': _('Add %s') % opts.verbose_name,
|
||||
@ -374,7 +374,7 @@ def change_stage(request, app_label, model_name, object_id):
|
||||
errors = {}
|
||||
|
||||
# Populate the FormWrapper.
|
||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
form.original = manipulator.original_object
|
||||
form.order_objects = []
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.core import validators
|
||||
from django import template, forms
|
||||
from django import template, oldforms
|
||||
from django.template import loader
|
||||
from django.shortcuts import render_to_response
|
||||
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.')
|
||||
return render_to_response('admin/template_validator.html', {
|
||||
'title': 'Template validator',
|
||||
'form': forms.FormWrapper(manipulator, new_data, errors),
|
||||
'form': oldforms.FormWrapper(manipulator, new_data, errors),
|
||||
}, context_instance=template.RequestContext(request))
|
||||
template_validator = staff_member_required(template_validator)
|
||||
|
||||
class TemplateValidator(forms.Manipulator):
|
||||
class TemplateValidator(oldforms.Manipulator):
|
||||
def __init__(self, settings_modules):
|
||||
self.settings_modules = settings_modules
|
||||
site_list = Site.objects.in_bulk(settings_modules.keys()).values()
|
||||
self.fields = (
|
||||
forms.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.SelectField('site', is_required=True, choices=[(s.id, s.name) for s in site_list]),
|
||||
oldforms.LargeTextField('template', is_required=True, rows=25, validator_list=[self.isValidTemplate]),
|
||||
)
|
||||
|
||||
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.template import Context, loader
|
||||
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."
|
||||
def __init__(self):
|
||||
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]),
|
||||
forms.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='password1', length=30, maxlength=60, is_required=True),
|
||||
oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
|
||||
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
|
||||
)
|
||||
|
||||
@ -27,7 +27,7 @@ class UserCreationForm(forms.Manipulator):
|
||||
"Creates the user."
|
||||
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
|
||||
username/password logins.
|
||||
@ -41,9 +41,9 @@ class AuthenticationForm(forms.Manipulator):
|
||||
"""
|
||||
self.request = request
|
||||
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]),
|
||||
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
|
||||
|
||||
@ -68,11 +68,11 @@ class AuthenticationForm(forms.Manipulator):
|
||||
def get_user(self):
|
||||
return self.user_cache
|
||||
|
||||
class PasswordResetForm(forms.Manipulator):
|
||||
class PasswordResetForm(oldforms.Manipulator):
|
||||
"A form that lets a user request a password reset"
|
||||
def __init__(self):
|
||||
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]),
|
||||
)
|
||||
|
||||
@ -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])
|
||||
|
||||
class PasswordChangeForm(forms.Manipulator):
|
||||
class PasswordChangeForm(oldforms.Manipulator):
|
||||
"A form that lets a user change his password."
|
||||
def __init__(self, user):
|
||||
self.user = user
|
||||
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]),
|
||||
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."))]),
|
||||
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):
|
||||
@ -126,3 +126,18 @@ class PasswordChangeForm(forms.Manipulator):
|
||||
"Saves the new password."
|
||||
self.user.set_password(new_data['new_password1'])
|
||||
self.user.save()
|
||||
|
||||
class AdminPasswordChangeForm(oldforms.Manipulator):
|
||||
"A form used to change the password of a user in the admin interface."
|
||||
def __init__(self, user):
|
||||
self.user = user
|
||||
self.fields = (
|
||||
oldforms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
|
||||
oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
|
||||
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
|
||||
)
|
||||
|
||||
def save(self, new_data):
|
||||
"Saves the new password."
|
||||
self.user.set_password(new_data['password1'])
|
||||
self.user.save()
|
||||
|
@ -91,7 +91,7 @@ class User(models.Model):
|
||||
first_name = models.CharField(_('first name'), maxlength=30, blank=True)
|
||||
last_name = models.CharField(_('last name'), maxlength=30, blank=True)
|
||||
email = models.EmailField(_('e-mail address'), blank=True)
|
||||
password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]'"))
|
||||
password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
|
||||
is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
|
||||
is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user can log into the Django admin. Unselect this instead of deleting accounts."))
|
||||
is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
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.template import RequestContext
|
||||
from django.contrib.sites.models import Site
|
||||
@ -26,7 +26,7 @@ def login(request, template_name='registration/login.html'):
|
||||
errors = {}
|
||||
request.session.set_test_cookie()
|
||||
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,
|
||||
'site_name': Site.objects.get_current().name,
|
||||
}, context_instance=RequestContext(request))
|
||||
@ -62,7 +62,7 @@ def password_reset(request, is_admin_site=False, template_name='registration/pas
|
||||
else:
|
||||
form.save(email_template_name=email_template_name)
|
||||
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))
|
||||
|
||||
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:
|
||||
form.save(new_data)
|
||||
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))
|
||||
password_change = login_required(password_change)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
from django.core import validators
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django.core.mail import mail_admins, mail_managers
|
||||
from django.http import Http404
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
@ -28,37 +28,37 @@ class PublicCommentManipulator(AuthenticationForm):
|
||||
else:
|
||||
return []
|
||||
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]),
|
||||
forms.RadioSelectField(field_name="rating1", choices=choices,
|
||||
oldforms.RadioSelectField(field_name="rating1", choices=choices,
|
||||
is_required=ratings_required and num_rating_choices > 0,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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())
|
||||
return c
|
||||
|
||||
class PublicFreeCommentManipulator(forms.Manipulator):
|
||||
class PublicFreeCommentManipulator(oldforms.Manipulator):
|
||||
"Manipulator that handles public free (unregistered) comments"
|
||||
def __init__(self):
|
||||
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]),
|
||||
forms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
||||
oldforms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
||||
validator_list=[self.hasNoProfanities]),
|
||||
)
|
||||
|
||||
@ -221,9 +221,9 @@ def post_comment(request):
|
||||
from django.contrib.auth import login
|
||||
login(request, manipulator.get_user())
|
||||
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):
|
||||
forms.FormWrapper.__init__(self, manipulator, new_data, errors)
|
||||
oldforms.FormWrapper.__init__(self, manipulator, new_data, errors)
|
||||
self.rating_choices = rating_choices
|
||||
def ratings(self):
|
||||
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)
|
||||
return render_to_response('comments/free_preview.html', {
|
||||
'comment': comment,
|
||||
'comment_form': forms.FormWrapper(manipulator, new_data, errors),
|
||||
'comment_form': oldforms.FormWrapper(manipulator, new_data, errors),
|
||||
'options': options,
|
||||
'target': target,
|
||||
'hash': security_hash,
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
CONTENT_TYPE_CACHE = {}
|
||||
class ContentTypeManager(models.Manager):
|
||||
def get_for_model(self, model):
|
||||
"""
|
||||
@ -8,10 +9,15 @@ class ContentTypeManager(models.Manager):
|
||||
ContentType if necessary.
|
||||
"""
|
||||
opts = model._meta
|
||||
# The str() is needed around opts.verbose_name because it's a
|
||||
# django.utils.functional.__proxy__ object.
|
||||
ct, created = self.model._default_manager.get_or_create(app_label=opts.app_label,
|
||||
model=opts.object_name.lower(), defaults={'name': str(opts.verbose_name)})
|
||||
key = (opts.app_label, opts.object_name.lower())
|
||||
try:
|
||||
ct = CONTENT_TYPE_CACHE[key]
|
||||
except KeyError:
|
||||
# The str() is needed around opts.verbose_name because it's a
|
||||
# django.utils.functional.__proxy__ object.
|
||||
ct, created = self.model._default_manager.get_or_create(app_label=key[0],
|
||||
model=key[1], defaults={'name': str(opts.verbose_name)})
|
||||
CONTENT_TYPE_CACHE[key] = ct
|
||||
return ct
|
||||
|
||||
class ContentType(models.Model):
|
||||
|
@ -11,7 +11,7 @@ import md5
|
||||
import re
|
||||
import itertools
|
||||
|
||||
_ERROR_MSG = "<h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p>"
|
||||
_ERROR_MSG = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>'
|
||||
|
||||
_POST_FORM_RE = \
|
||||
re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
|
||||
|
@ -48,6 +48,7 @@ from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template.context import RequestContext
|
||||
import cPickle as pickle
|
||||
import md5
|
||||
|
||||
@ -91,7 +92,9 @@ class FormPreview(object):
|
||||
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})
|
||||
return render_to_response(self.form_template,
|
||||
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def preview_post(self, request):
|
||||
"Validates the POST data. If valid, displays the preview page. Else, redisplays form."
|
||||
@ -100,9 +103,9 @@ class FormPreview(object):
|
||||
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)
|
||||
return render_to_response(self.preview_template, context, context_instance=RequestContext(request))
|
||||
else:
|
||||
return render_to_response(self.form_template, context)
|
||||
return render_to_response(self.form_template, context, context_instance=RequestContext(request))
|
||||
|
||||
def post_post(self, request):
|
||||
"Validates the POST data. If valid, calls done(). Else, redisplays form."
|
||||
@ -112,7 +115,9 @@ class FormPreview(object):
|
||||
return self.failed_hash(request) # Security hash failed.
|
||||
return self.done(request, f.clean_data)
|
||||
else:
|
||||
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state})
|
||||
return render_to_response(self.form_template,
|
||||
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.models import Session
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.utils.cache import patch_vary_headers
|
||||
import datetime
|
||||
|
||||
@ -55,7 +56,7 @@ class SessionWrapper(object):
|
||||
s = Session.objects.get(session_key=self.session_key,
|
||||
expire_date__gt=datetime.datetime.now())
|
||||
self._session_cache = s.get_decoded()
|
||||
except Session.DoesNotExist:
|
||||
except (Session.DoesNotExist, SuspiciousOperation):
|
||||
self._session_cache = {}
|
||||
# Set the session_key to None to force creation of a new
|
||||
# key, for extra security.
|
||||
|
6
django/core/cache/backends/dummy.py
vendored
6
django/core/cache/backends/dummy.py
vendored
@ -6,8 +6,8 @@ class CacheClass(BaseCache):
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
pass
|
||||
def get(self, key, default=None):
|
||||
return default
|
||||
|
||||
def set(self, *args, **kwargs):
|
||||
pass
|
||||
@ -16,7 +16,7 @@ class CacheClass(BaseCache):
|
||||
pass
|
||||
|
||||
def get_many(self, *args, **kwargs):
|
||||
pass
|
||||
return {}
|
||||
|
||||
def has_key(self, *args, **kwargs):
|
||||
return False
|
||||
|
@ -60,7 +60,10 @@ class BaseHandler(object):
|
||||
if response:
|
||||
return response
|
||||
|
||||
resolver = urlresolvers.RegexURLResolver(r'^/', settings.ROOT_URLCONF)
|
||||
# Get urlconf from request object, if available. Otherwise use default.
|
||||
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
|
||||
|
||||
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
|
||||
try:
|
||||
callback, callback_args, callback_kwargs = resolver.resolve(request.path)
|
||||
|
||||
|
@ -25,7 +25,7 @@ APP_ARGS = '[appname ...]'
|
||||
# which has been installed.
|
||||
PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
|
||||
|
||||
INVALID_PROJECT_NAMES = ('django', 'test')
|
||||
INVALID_PROJECT_NAMES = ('django', 'site', 'test')
|
||||
|
||||
# Set up the terminal color scheme.
|
||||
class dummy: pass
|
||||
@ -708,7 +708,7 @@ def startproject(project_name, directory):
|
||||
"Creates a Django project for the given project_name in the given directory."
|
||||
from random import choice
|
||||
if project_name in INVALID_PROJECT_NAMES:
|
||||
sys.stderr.write(style.ERROR("Error: %r isn't a valid project name. Please try another.\n" % project_name))
|
||||
sys.stderr.write(style.ERROR("Error: '%r' conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name.\n" % project_name))
|
||||
sys.exit(1)
|
||||
_start_helper('project', project_name, directory)
|
||||
# Create a random SECRET_KEY hash, and put it in the main settings.
|
||||
|
@ -57,7 +57,7 @@ def Deserializer(object_list, **options):
|
||||
for d in object_list:
|
||||
# Look up the model and starting build a dict of data for it.
|
||||
Model = _get_model(d["model"])
|
||||
data = {Model._meta.pk.name : d["pk"]}
|
||||
data = {Model._meta.pk.attname : d["pk"]}
|
||||
m2m_data = {}
|
||||
|
||||
# Handle each field
|
||||
|
@ -21,6 +21,5 @@ DATA_TYPES = {
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'text',
|
||||
'TimeField': 'time',
|
||||
'URLField': 'varchar(200)',
|
||||
'USStateField': 'varchar(2)',
|
||||
}
|
||||
|
@ -98,9 +98,11 @@ class DatabaseWrapper(local):
|
||||
kwargs['port'] = int(settings.DATABASE_PORT)
|
||||
kwargs.update(self.options)
|
||||
self.connection = Database.connect(**kwargs)
|
||||
cursor = self.connection.cursor()
|
||||
if self.connection.get_server_info() >= '4.1':
|
||||
cursor.execute("SET NAMES 'utf8'")
|
||||
cursor = self.connection.cursor()
|
||||
if self.connection.get_server_info() >= '4.1':
|
||||
cursor.execute("SET NAMES 'utf8'")
|
||||
else:
|
||||
cursor = self.connection.cursor()
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
|
||||
return cursor
|
||||
|
@ -25,6 +25,5 @@ DATA_TYPES = {
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'longtext',
|
||||
'TimeField': 'time',
|
||||
'URLField': 'varchar(200)',
|
||||
'USStateField': 'varchar(2)',
|
||||
}
|
||||
|
@ -21,6 +21,5 @@ DATA_TYPES = {
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'long',
|
||||
'TimeField': 'timestamp',
|
||||
'URLField': 'varchar(200)',
|
||||
'USStateField': 'varchar(2)',
|
||||
}
|
||||
|
@ -20,6 +20,38 @@ except ImportError:
|
||||
# Import copy of _thread_local.py from Python 2.4
|
||||
from django.utils._threading_local import local
|
||||
|
||||
def smart_basestring(s, charset):
|
||||
if isinstance(s, unicode):
|
||||
return s.encode(charset)
|
||||
return s
|
||||
|
||||
class UnicodeCursorWrapper(object):
|
||||
"""
|
||||
A thin wrapper around psycopg cursors that allows them to accept Unicode
|
||||
strings as params.
|
||||
|
||||
This is necessary because psycopg doesn't apply any DB quoting to
|
||||
parameters that are Unicode strings. If a param is Unicode, this will
|
||||
convert it to a bytestring using DEFAULT_CHARSET before passing it to
|
||||
psycopg.
|
||||
"""
|
||||
def __init__(self, cursor, charset):
|
||||
self.cursor = cursor
|
||||
self.charset = charset
|
||||
|
||||
def execute(self, sql, params=()):
|
||||
return self.cursor.execute(sql, [smart_basestring(p, self.charset) for p in params])
|
||||
|
||||
def executemany(self, sql, param_list):
|
||||
new_param_list = [tuple([smart_basestring(p, self.charset) for p in params]) for params in param_list]
|
||||
return self.cursor.executemany(sql, new_param_list)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if self.__dict__.has_key(attr):
|
||||
return self.__dict__[attr]
|
||||
else:
|
||||
return getattr(self.cursor, attr)
|
||||
|
||||
class DatabaseWrapper(local):
|
||||
def __init__(self, **kwargs):
|
||||
self.connection = None
|
||||
@ -45,6 +77,7 @@ class DatabaseWrapper(local):
|
||||
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
|
||||
cursor = self.connection.cursor()
|
||||
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
||||
cursor = UnicodeCursorWrapper(cursor, settings.DEFAULT_CHARSET)
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
return cursor
|
||||
@ -118,7 +151,7 @@ def get_pk_default_value():
|
||||
try:
|
||||
Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
|
||||
except AttributeError:
|
||||
raise Exception, "You appear to be using psycopg version 2, which isn't supported yet, because it's still in beta. Use psycopg version 1 instead: http://initd.org/projects/psycopg1"
|
||||
raise Exception, "You appear to be using psycopg version 2. Set your DATABASE_ENGINE to 'postgresql_psycopg2' instead of 'postgresql'."
|
||||
Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time))
|
||||
Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp))
|
||||
Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean))
|
||||
|
@ -25,6 +25,5 @@ DATA_TYPES = {
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'text',
|
||||
'TimeField': 'time',
|
||||
'URLField': 'varchar(200)',
|
||||
'USStateField': 'varchar(2)',
|
||||
}
|
||||
|
@ -24,6 +24,5 @@ DATA_TYPES = {
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'text',
|
||||
'TimeField': 'time',
|
||||
'URLField': 'varchar(200)',
|
||||
'USStateField': 'varchar(2)',
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ from django.db.models import signals
|
||||
from django.dispatch import dispatcher
|
||||
from django.conf import settings
|
||||
from django.core import validators
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django import newforms as forms
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.functional import curry
|
||||
from django.utils.itercompat import tee
|
||||
@ -206,10 +207,10 @@ class Field(object):
|
||||
|
||||
if self.choices:
|
||||
if self.radio_admin:
|
||||
field_objs = [forms.RadioSelectField]
|
||||
field_objs = [oldforms.RadioSelectField]
|
||||
params['ul_class'] = get_ul_class(self.radio_admin)
|
||||
else:
|
||||
field_objs = [forms.SelectField]
|
||||
field_objs = [oldforms.SelectField]
|
||||
|
||||
params['choices'] = self.get_choices_default()
|
||||
else:
|
||||
@ -218,7 +219,7 @@ class Field(object):
|
||||
|
||||
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.
|
||||
|
||||
name_prefix is a prefix to prepend to the "field_name" argument.
|
||||
@ -333,6 +334,16 @@ class Field(object):
|
||||
return self._choices
|
||||
choices = property(_get_choices)
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
"Returns a django.newforms.Field instance for this database Field."
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.CharField(**defaults)
|
||||
|
||||
def value_from_object(self, obj):
|
||||
"Returns the value of this field in the given model instance."
|
||||
return getattr(obj, self.attname)
|
||||
|
||||
class AutoField(Field):
|
||||
empty_strings_allowed = False
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -354,7 +365,7 @@ class AutoField(Field):
|
||||
return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.HiddenField]
|
||||
return [oldforms.HiddenField]
|
||||
|
||||
def get_manipulator_new_data(self, new_data, rel=False):
|
||||
# Never going to be called
|
||||
@ -369,6 +380,9 @@ class AutoField(Field):
|
||||
super(AutoField, self).contribute_to_class(cls, name)
|
||||
cls._meta.has_auto_field = True
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
return None
|
||||
|
||||
class BooleanField(Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['blank'] = True
|
||||
@ -381,11 +395,16 @@ class BooleanField(Field):
|
||||
raise validators.ValidationError, gettext("This value must be either True or False.")
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.CheckboxField]
|
||||
return [oldforms.CheckboxField]
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.BooleanField(**defaults)
|
||||
|
||||
class CharField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.TextField]
|
||||
return [oldforms.TextField]
|
||||
|
||||
def to_python(self, value):
|
||||
if isinstance(value, basestring):
|
||||
@ -397,10 +416,15 @@ class CharField(Field):
|
||||
raise validators.ValidationError, gettext_lazy("This field cannot be null.")
|
||||
return str(value)
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'max_length': self.maxlength, 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.CharField(**defaults)
|
||||
|
||||
# TODO: Maybe move this into contrib, because it's specialized.
|
||||
class CommaSeparatedIntegerField(CharField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.CommaSeparatedIntegerField]
|
||||
return [oldforms.CommaSeparatedIntegerField]
|
||||
|
||||
class DateField(Field):
|
||||
empty_strings_allowed = False
|
||||
@ -462,12 +486,17 @@ class DateField(Field):
|
||||
return Field.get_db_prep_save(self, value)
|
||||
|
||||
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)
|
||||
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.DateField(**defaults)
|
||||
|
||||
class DateTimeField(DateField):
|
||||
def to_python(self, value):
|
||||
if isinstance(value, datetime.datetime):
|
||||
@ -503,7 +532,7 @@ class DateTimeField(DateField):
|
||||
return Field.get_db_prep_lookup(self, lookup_type, value)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.DateField, forms.TimeField]
|
||||
return [oldforms.DateField, oldforms.TimeField]
|
||||
|
||||
def get_manipulator_field_names(self, name_prefix):
|
||||
return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
|
||||
@ -526,6 +555,11 @@ class DateTimeField(DateField):
|
||||
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
|
||||
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.DateTimeField(**defaults)
|
||||
|
||||
class EmailField(CharField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['maxlength'] = 75
|
||||
@ -535,11 +569,16 @@ class EmailField(CharField):
|
||||
return "CharField"
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.EmailField]
|
||||
return [oldforms.EmailField]
|
||||
|
||||
def validate(self, field_data, all_data):
|
||||
validators.isValidEmail(field_data, all_data)
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.EmailField(**defaults)
|
||||
|
||||
class FileField(Field):
|
||||
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
|
||||
self.upload_to = upload_to
|
||||
@ -599,7 +638,7 @@ class FileField(Field):
|
||||
os.remove(file_name)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.FileUploadField, forms.HiddenField]
|
||||
return [oldforms.FileUploadField, oldforms.HiddenField]
|
||||
|
||||
def get_manipulator_field_names(self, name_prefix):
|
||||
return [name_prefix + self.name + '_file', name_prefix + self.name]
|
||||
@ -627,7 +666,7 @@ class FilePathField(Field):
|
||||
Field.__init__(self, verbose_name, name, **kwargs)
|
||||
|
||||
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):
|
||||
empty_strings_allowed = False
|
||||
@ -636,7 +675,7 @@ class FloatField(Field):
|
||||
Field.__init__(self, verbose_name, name, **kwargs)
|
||||
|
||||
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):
|
||||
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
|
||||
@ -644,7 +683,7 @@ class ImageField(FileField):
|
||||
FileField.__init__(self, verbose_name, name, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.ImageUploadField, forms.HiddenField]
|
||||
return [oldforms.ImageUploadField, oldforms.HiddenField]
|
||||
|
||||
def contribute_to_class(self, cls, name):
|
||||
super(ImageField, self).contribute_to_class(cls, name)
|
||||
@ -670,7 +709,12 @@ class ImageField(FileField):
|
||||
class IntegerField(Field):
|
||||
empty_strings_allowed = False
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.IntegerField]
|
||||
return [oldforms.IntegerField]
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.IntegerField(**defaults)
|
||||
|
||||
class IPAddressField(Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -678,7 +722,7 @@ class IPAddressField(Field):
|
||||
Field.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.IPAddressField]
|
||||
return [oldforms.IPAddressField]
|
||||
|
||||
def validate(self, field_data, all_data):
|
||||
validators.isValidIPAddress4(field_data, None)
|
||||
@ -689,22 +733,22 @@ class NullBooleanField(Field):
|
||||
Field.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.NullBooleanField]
|
||||
return [oldforms.NullBooleanField]
|
||||
|
||||
class PhoneNumberField(IntegerField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.PhoneNumberField]
|
||||
return [oldforms.PhoneNumberField]
|
||||
|
||||
def validate(self, field_data, all_data):
|
||||
validators.isValidPhone(field_data, all_data)
|
||||
|
||||
class PositiveIntegerField(IntegerField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.PositiveIntegerField]
|
||||
return [oldforms.PositiveIntegerField]
|
||||
|
||||
class PositiveSmallIntegerField(IntegerField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.PositiveSmallIntegerField]
|
||||
return [oldforms.PositiveSmallIntegerField]
|
||||
|
||||
class SlugField(Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -716,15 +760,20 @@ class SlugField(Field):
|
||||
Field.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.TextField]
|
||||
return [oldforms.TextField]
|
||||
|
||||
class SmallIntegerField(IntegerField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.SmallIntegerField]
|
||||
return [oldforms.SmallIntegerField]
|
||||
|
||||
class TextField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.LargeTextField]
|
||||
return [oldforms.LargeTextField]
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'widget': forms.Textarea, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.CharField(**defaults)
|
||||
|
||||
class TimeField(Field):
|
||||
empty_strings_allowed = False
|
||||
@ -760,24 +809,39 @@ class TimeField(Field):
|
||||
return Field.get_db_prep_save(self, value)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.TimeField]
|
||||
return [oldforms.TimeField]
|
||||
|
||||
def flatten_data(self,follow, obj = None):
|
||||
val = self._get_val_from_obj(obj)
|
||||
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||
|
||||
class URLField(Field):
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.TimeField(**defaults)
|
||||
|
||||
class URLField(CharField):
|
||||
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
|
||||
kwargs['maxlength'] = kwargs.get('maxlength', 200)
|
||||
if verify_exists:
|
||||
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
|
||||
Field.__init__(self, verbose_name, name, **kwargs)
|
||||
self.verify_exists = verify_exists
|
||||
CharField.__init__(self, verbose_name, name, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.URLField]
|
||||
return [oldforms.URLField]
|
||||
|
||||
def get_internal_type(self):
|
||||
return "CharField"
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'verify_exists': self.verify_exists, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.URLField(**defaults)
|
||||
|
||||
class USStateField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.USStateField]
|
||||
return [oldforms.USStateField]
|
||||
|
||||
class XMLField(TextField):
|
||||
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
|
||||
@ -788,7 +852,7 @@ class XMLField(TextField):
|
||||
return "TextField"
|
||||
|
||||
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):
|
||||
empty_strings_allowed=False
|
||||
@ -801,4 +865,4 @@ class OrderingField(IntegerField):
|
||||
return "IntegerField"
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import backend
|
||||
from django.db.models import signals
|
||||
@ -98,7 +98,7 @@ class GenericRelation(RelatedField, Field):
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
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):
|
||||
return Field.get_choices(self, include_blank=False)
|
||||
|
@ -2,10 +2,12 @@ from django.db import backend, transaction
|
||||
from django.db.models import signals, get_model
|
||||
from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class
|
||||
from django.db.models.related import RelatedObject
|
||||
from django.utils.text import capfirst
|
||||
from django.utils.translation import gettext_lazy, string_concat, ngettext
|
||||
from django.utils.functional import curry
|
||||
from django.core import validators
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django import newforms as forms
|
||||
from django.dispatch import dispatcher
|
||||
|
||||
# For Python 2.3
|
||||
@ -256,8 +258,7 @@ class ForeignRelatedObjectsDescriptor(object):
|
||||
# Otherwise, just move the named objects into the set.
|
||||
if self.related.field.null:
|
||||
manager.clear()
|
||||
for obj in value:
|
||||
manager.add(obj)
|
||||
manager.add(*value)
|
||||
|
||||
def create_many_related_manager(superclass):
|
||||
"""Creates a manager that subclasses 'superclass' (which is a Manager)
|
||||
@ -315,28 +316,36 @@ def create_many_related_manager(superclass):
|
||||
# join_table: name of the m2m link table
|
||||
# source_col_name: the PK colname in join_table for the source object
|
||||
# target_col_name: the PK colname in join_table for the target object
|
||||
# *objs - objects to add
|
||||
# *objs - objects to add. Either object instances, or primary keys of object instances.
|
||||
from django.db import connection
|
||||
|
||||
# Add the newly created or already existing objects to the join table.
|
||||
# First find out which items are already added, to avoid adding them twice
|
||||
new_ids = set([obj._get_pk_val() for obj in objs])
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||
(target_col_name, self.join_table, source_col_name,
|
||||
target_col_name, ",".join(['%s'] * len(new_ids))),
|
||||
[self._pk_val] + list(new_ids))
|
||||
if cursor.rowcount is not None and cursor.rowcount != 0:
|
||||
existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)])
|
||||
else:
|
||||
existing_ids = set()
|
||||
# If there aren't any objects, there is nothing to do.
|
||||
if objs:
|
||||
# Check that all the objects are of the right type
|
||||
new_ids = set()
|
||||
for obj in objs:
|
||||
if isinstance(obj, self.model):
|
||||
new_ids.add(obj._get_pk_val())
|
||||
else:
|
||||
new_ids.add(obj)
|
||||
# Add the newly created or already existing objects to the join table.
|
||||
# First find out which items are already added, to avoid adding them twice
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||
(target_col_name, self.join_table, source_col_name,
|
||||
target_col_name, ",".join(['%s'] * len(new_ids))),
|
||||
[self._pk_val] + list(new_ids))
|
||||
if cursor.rowcount is not None and cursor.rowcount != 0:
|
||||
existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)])
|
||||
else:
|
||||
existing_ids = set()
|
||||
|
||||
# Add the ones that aren't there already
|
||||
for obj_id in (new_ids - existing_ids):
|
||||
cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
|
||||
(self.join_table, source_col_name, target_col_name),
|
||||
[self._pk_val, obj_id])
|
||||
transaction.commit_unless_managed()
|
||||
# Add the ones that aren't there already
|
||||
for obj_id in (new_ids - existing_ids):
|
||||
cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
|
||||
(self.join_table, source_col_name, target_col_name),
|
||||
[self._pk_val, obj_id])
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
def _remove_items(self, source_col_name, target_col_name, *objs):
|
||||
# source_col_name: the PK colname in join_table for the source object
|
||||
@ -344,16 +353,22 @@ def create_many_related_manager(superclass):
|
||||
# *objs - objects to remove
|
||||
from django.db import connection
|
||||
|
||||
for obj in objs:
|
||||
if not isinstance(obj, self.model):
|
||||
raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name
|
||||
# Remove the specified objects from the join table
|
||||
cursor = connection.cursor()
|
||||
for obj in objs:
|
||||
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \
|
||||
(self.join_table, source_col_name, target_col_name),
|
||||
[self._pk_val, obj._get_pk_val()])
|
||||
transaction.commit_unless_managed()
|
||||
# If there aren't any objects, there is nothing to do.
|
||||
if objs:
|
||||
# Check that all the objects are of the right type
|
||||
old_ids = set()
|
||||
for obj in objs:
|
||||
if isinstance(obj, self.model):
|
||||
old_ids.add(obj._get_pk_val())
|
||||
else:
|
||||
old_ids.add(obj)
|
||||
# Remove the specified objects from the join table
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||
(self.join_table, source_col_name,
|
||||
target_col_name, ",".join(['%s'] * len(old_ids))),
|
||||
[self._pk_val] + list(old_ids))
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
def _clear_items(self, source_col_name):
|
||||
# source_col_name: the PK colname in join_table for the source object
|
||||
@ -405,8 +420,7 @@ class ManyRelatedObjectsDescriptor(object):
|
||||
|
||||
manager = self.__get__(instance)
|
||||
manager.clear()
|
||||
for obj in value:
|
||||
manager.add(obj)
|
||||
manager.add(*value)
|
||||
|
||||
class ReverseManyRelatedObjectsDescriptor(object):
|
||||
# This class provides the functionality that makes the related-object
|
||||
@ -447,8 +461,7 @@ class ReverseManyRelatedObjectsDescriptor(object):
|
||||
|
||||
manager = self.__get__(instance)
|
||||
manager.clear()
|
||||
for obj in value:
|
||||
manager.add(obj)
|
||||
manager.add(*value)
|
||||
|
||||
class ForeignKey(RelatedField, Field):
|
||||
empty_strings_allowed = False
|
||||
@ -493,13 +506,13 @@ class ForeignKey(RelatedField, Field):
|
||||
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
|
||||
else:
|
||||
if self.radio_admin:
|
||||
field_objs = [forms.RadioSelectField]
|
||||
field_objs = [oldforms.RadioSelectField]
|
||||
params['ul_class'] = get_ul_class(self.radio_admin)
|
||||
else:
|
||||
if self.null:
|
||||
field_objs = [forms.NullSelectField]
|
||||
field_objs = [oldforms.NullSelectField]
|
||||
else:
|
||||
field_objs = [forms.SelectField]
|
||||
field_objs = [oldforms.SelectField]
|
||||
params['choices'] = self.get_choices_default()
|
||||
return field_objs, params
|
||||
|
||||
@ -508,7 +521,7 @@ class ForeignKey(RelatedField, Field):
|
||||
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
|
||||
return rel_field.get_manipulator_field_objs()
|
||||
else:
|
||||
return [forms.IntegerField]
|
||||
return [oldforms.IntegerField]
|
||||
|
||||
def get_db_prep_save(self, value):
|
||||
if value == '' or value == None:
|
||||
@ -539,6 +552,11 @@ class ForeignKey(RelatedField, Field):
|
||||
def contribute_to_related_class(self, cls, related):
|
||||
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.ChoiceField(**defaults)
|
||||
|
||||
class OneToOneField(RelatedField, IntegerField):
|
||||
def __init__(self, to, to_field=None, **kwargs):
|
||||
try:
|
||||
@ -581,13 +599,13 @@ class OneToOneField(RelatedField, IntegerField):
|
||||
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
|
||||
else:
|
||||
if self.radio_admin:
|
||||
field_objs = [forms.RadioSelectField]
|
||||
field_objs = [oldforms.RadioSelectField]
|
||||
params['ul_class'] = get_ul_class(self.radio_admin)
|
||||
else:
|
||||
if self.null:
|
||||
field_objs = [forms.NullSelectField]
|
||||
field_objs = [oldforms.NullSelectField]
|
||||
else:
|
||||
field_objs = [forms.SelectField]
|
||||
field_objs = [oldforms.SelectField]
|
||||
params['choices'] = self.get_choices_default()
|
||||
return field_objs, params
|
||||
|
||||
@ -600,6 +618,11 @@ class OneToOneField(RelatedField, IntegerField):
|
||||
if not cls._meta.one_to_one_field:
|
||||
cls._meta.one_to_one_field = self
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.ChoiceField(**kwargs)
|
||||
|
||||
class ManyToManyField(RelatedField, Field):
|
||||
def __init__(self, to, **kwargs):
|
||||
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
|
||||
@ -610,6 +633,7 @@ class ManyToManyField(RelatedField, Field):
|
||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||
raw_id_admin=kwargs.pop('raw_id_admin', False),
|
||||
symmetrical=kwargs.pop('symmetrical', True))
|
||||
self.db_table = kwargs.pop('db_table', None)
|
||||
if kwargs["rel"].raw_id_admin:
|
||||
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
|
||||
Field.__init__(self, **kwargs)
|
||||
@ -622,17 +646,20 @@ class ManyToManyField(RelatedField, Field):
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
if self.rel.raw_id_admin:
|
||||
return [forms.RawIdAdminField]
|
||||
return [oldforms.RawIdAdminField]
|
||||
else:
|
||||
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):
|
||||
return Field.get_choices(self, include_blank=False)
|
||||
|
||||
def _get_m2m_db_table(self, opts):
|
||||
"Function that can be curried to provide the m2m table name for this relation"
|
||||
return '%s_%s' % (opts.db_table, self.name)
|
||||
if self.db_table:
|
||||
return self.db_table
|
||||
else:
|
||||
return '%s_%s' % (opts.db_table, self.name)
|
||||
|
||||
def _get_m2m_column_name(self, related):
|
||||
"Function that can be curried to provide the source column name for the m2m table"
|
||||
@ -706,6 +733,19 @@ class ManyToManyField(RelatedField, Field):
|
||||
def set_attributes_from_rel(self):
|
||||
pass
|
||||
|
||||
def value_from_object(self, obj):
|
||||
"Returns the value of this field in the given model instance."
|
||||
return getattr(obj, self.attname).all()
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
# If initial is passed in, it's a list of related objects, but the
|
||||
# MultipleChoiceField takes a list of IDs.
|
||||
if kwargs.get('initial') is not None:
|
||||
kwargs['initial'] = [i._get_pk_val() for i in kwargs['initial']]
|
||||
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.MultipleChoiceField(**defaults)
|
||||
|
||||
class ManyToOneRel(object):
|
||||
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
||||
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.db.models.query import QuerySet
|
||||
from django.db.models.query import QuerySet, EmptyQuerySet
|
||||
from django.dispatch import dispatcher
|
||||
from django.db.models import signals
|
||||
from django.db.models.fields import FieldDoesNotExist
|
||||
@ -41,12 +41,18 @@ class Manager(object):
|
||||
#######################
|
||||
# PROXIES TO QUERYSET #
|
||||
#######################
|
||||
|
||||
def get_empty_query_set(self):
|
||||
return EmptyQuerySet(self.model)
|
||||
|
||||
def get_query_set(self):
|
||||
"""Returns a new QuerySet object. Subclasses can override this method
|
||||
to easily customise the behaviour of the Manager.
|
||||
"""
|
||||
return QuerySet(self.model)
|
||||
|
||||
def none(self):
|
||||
return self.get_empty_query_set()
|
||||
|
||||
def all(self):
|
||||
return self.get_query_set()
|
||||
|
@ -1,5 +1,5 @@
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django.core import validators
|
||||
from django.db.models.fields import FileField, AutoField
|
||||
from django.dispatch import dispatcher
|
||||
@ -40,7 +40,7 @@ class ManipulatorDescriptor(object):
|
||||
self.man._prepare(model)
|
||||
return self.man
|
||||
|
||||
class AutomaticManipulator(forms.Manipulator):
|
||||
class AutomaticManipulator(oldforms.Manipulator):
|
||||
def _prepare(cls, model):
|
||||
cls.model = model
|
||||
cls.manager = model._default_manager
|
||||
@ -76,7 +76,7 @@ class AutomaticManipulator(forms.Manipulator):
|
||||
|
||||
# Add field for ordering.
|
||||
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):
|
||||
# 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):
|
||||
from django.db.models.fields.related import ManyToOneRel
|
||||
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:
|
||||
return # Date was invalid. This will be caught by another validator.
|
||||
lookup_kwargs = {'%s__year' % date_field.name: date_val.year}
|
||||
|
@ -1,5 +1,6 @@
|
||||
from django.db import backend, connection, transaction
|
||||
from django.db.models.fields import DateField, FieldDoesNotExist
|
||||
from django.db.models.fields.generic import GenericRelation
|
||||
from django.db.models import signals
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils.datastructures import SortedDict
|
||||
@ -25,6 +26,9 @@ QUERY_TERMS = (
|
||||
# Larger values are slightly faster at the expense of more storage space.
|
||||
GET_ITERATOR_CHUNK_SIZE = 100
|
||||
|
||||
class EmptyResultSet(Exception):
|
||||
pass
|
||||
|
||||
####################
|
||||
# HELPER FUNCTIONS #
|
||||
####################
|
||||
@ -168,7 +172,12 @@ class QuerySet(object):
|
||||
extra_select = self._select.items()
|
||||
|
||||
cursor = connection.cursor()
|
||||
select, sql, params = self._get_sql_clause()
|
||||
|
||||
try:
|
||||
select, sql, params = self._get_sql_clause()
|
||||
except EmptyResultSet:
|
||||
raise StopIteration
|
||||
|
||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||
fill_cache = self._select_related
|
||||
index_end = len(self.model._meta.fields)
|
||||
@ -192,7 +201,12 @@ class QuerySet(object):
|
||||
counter._offset = None
|
||||
counter._limit = None
|
||||
counter._select_related = False
|
||||
select, sql, params = counter._get_sql_clause()
|
||||
|
||||
try:
|
||||
select, sql, params = counter._get_sql_clause()
|
||||
except EmptyResultSet:
|
||||
return 0
|
||||
|
||||
cursor = connection.cursor()
|
||||
if self._distinct:
|
||||
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
|
||||
@ -523,7 +537,12 @@ class ValuesQuerySet(QuerySet):
|
||||
field_names = [f.attname for f in self.model._meta.fields]
|
||||
|
||||
cursor = connection.cursor()
|
||||
select, sql, params = self._get_sql_clause()
|
||||
|
||||
try:
|
||||
select, sql, params = self._get_sql_clause()
|
||||
except EmptyResultSet:
|
||||
raise StopIteration
|
||||
|
||||
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
|
||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||
while 1:
|
||||
@ -545,7 +564,12 @@ class DateQuerySet(QuerySet):
|
||||
if self._field.null:
|
||||
self._where.append('%s.%s IS NOT NULL' % \
|
||||
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
|
||||
select, sql, params = self._get_sql_clause()
|
||||
|
||||
try:
|
||||
select, sql, params = self._get_sql_clause()
|
||||
except EmptyResultSet:
|
||||
raise StopIteration
|
||||
|
||||
sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \
|
||||
(backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
|
||||
backend.quote_name(self._field.column))), sql, self._order)
|
||||
@ -562,6 +586,25 @@ class DateQuerySet(QuerySet):
|
||||
c._kind = self._kind
|
||||
c._order = self._order
|
||||
return c
|
||||
|
||||
class EmptyQuerySet(QuerySet):
|
||||
def __init__(self, model=None):
|
||||
super(EmptyQuerySet, self).__init__(model)
|
||||
self._result_cache = []
|
||||
|
||||
def iterator(self):
|
||||
raise StopIteration
|
||||
|
||||
def count(self):
|
||||
return 0
|
||||
|
||||
def delete(self):
|
||||
pass
|
||||
|
||||
def _clone(self, klass=None, **kwargs):
|
||||
c = super(EmptyQuerySet, self)._clone(klass, **kwargs)
|
||||
c._result_cache = []
|
||||
return c
|
||||
|
||||
class QOperator(object):
|
||||
"Base class for QAnd and QOr"
|
||||
@ -571,10 +614,14 @@ class QOperator(object):
|
||||
def get_sql(self, opts):
|
||||
joins, where, params = SortedDict(), [], []
|
||||
for val in self.args:
|
||||
joins2, where2, params2 = val.get_sql(opts)
|
||||
joins.update(joins2)
|
||||
where.extend(where2)
|
||||
params.extend(params2)
|
||||
try:
|
||||
joins2, where2, params2 = val.get_sql(opts)
|
||||
joins.update(joins2)
|
||||
where.extend(where2)
|
||||
params.extend(params2)
|
||||
except EmptyResultSet:
|
||||
if not isinstance(self, QOr):
|
||||
raise EmptyResultSet
|
||||
if where:
|
||||
return joins, ['(%s)' % self.operator.join(where)], params
|
||||
return joins, [], params
|
||||
@ -628,8 +675,11 @@ class QNot(Q):
|
||||
self.q = q
|
||||
|
||||
def get_sql(self, opts):
|
||||
joins, where, params = self.q.get_sql(opts)
|
||||
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
||||
try:
|
||||
joins, where, params = self.q.get_sql(opts)
|
||||
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
||||
except EmptyResultSet:
|
||||
return SortedDict(), [], []
|
||||
return joins, where2, params
|
||||
|
||||
def get_where_clause(lookup_type, table_prefix, field_name, value):
|
||||
@ -641,7 +691,11 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
|
||||
except KeyError:
|
||||
pass
|
||||
if lookup_type == 'in':
|
||||
return '%s%s IN (%s)' % (table_prefix, field_name, ','.join(['%s' for v in value]))
|
||||
in_string = ','.join(['%s' for id in value])
|
||||
if in_string:
|
||||
return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
|
||||
else:
|
||||
raise EmptyResultSet
|
||||
elif lookup_type == 'range':
|
||||
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
|
||||
elif lookup_type in ('year', 'month', 'day'):
|
||||
@ -926,18 +980,26 @@ def delete_objects(seen_objs):
|
||||
|
||||
pk_list = [pk for pk,instance in seen_objs[cls]]
|
||||
for related in cls._meta.get_all_related_many_to_many_objects():
|
||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||
(qn(related.field.m2m_db_table()),
|
||||
qn(related.field.m2m_reverse_name()),
|
||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
||||
if not isinstance(related.field, GenericRelation):
|
||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||
(qn(related.field.m2m_db_table()),
|
||||
qn(related.field.m2m_reverse_name()),
|
||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
||||
for f in cls._meta.many_to_many:
|
||||
if isinstance(f, GenericRelation):
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
|
||||
args_extra = [ContentType.objects.get_for_model(cls).id]
|
||||
else:
|
||||
query_extra = ''
|
||||
args_extra = []
|
||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||
cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
|
||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra,
|
||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
|
||||
for field in cls._meta.fields:
|
||||
if field.rel and field.null and field.rel.to in seen_objs:
|
||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,5 +13,5 @@ TODO:
|
||||
from util import ValidationError
|
||||
from widgets import *
|
||||
from fields import *
|
||||
from forms import Form
|
||||
from forms import *
|
||||
from models import *
|
||||
|
1
django/newforms/extras/__init__.py
Normal file
1
django/newforms/extras/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from widgets import *
|
59
django/newforms/extras/widgets.py
Normal file
59
django/newforms/extras/widgets.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""
|
||||
Extra HTML Widget classes
|
||||
"""
|
||||
|
||||
from django.newforms.widgets import Widget, Select
|
||||
from django.utils.dates import MONTHS
|
||||
import datetime
|
||||
|
||||
__all__ = ('SelectDateWidget',)
|
||||
|
||||
class SelectDateWidget(Widget):
|
||||
"""
|
||||
A Widget that splits date input into three <select> boxes.
|
||||
|
||||
This also serves as an example of a Widget that has more than one HTML
|
||||
element and hence implements value_from_datadict.
|
||||
"""
|
||||
month_field = '%s_month'
|
||||
day_field = '%s_day'
|
||||
year_field = '%s_year'
|
||||
|
||||
def __init__(self, attrs=None, years=None):
|
||||
# years is an optional list/tuple of years to use in the "year" select box.
|
||||
self.attrs = attrs or {}
|
||||
if years:
|
||||
self.years = years
|
||||
else:
|
||||
this_year = datetime.date.today().year
|
||||
self.years = range(this_year, this_year+10)
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
try:
|
||||
value = datetime.date(*map(int, value.split('-')))
|
||||
year_val, month_val, day_val = value.year, value.month, value.day
|
||||
except (AttributeError, TypeError, ValueError):
|
||||
year_val = month_val = day_val = None
|
||||
|
||||
output = []
|
||||
|
||||
month_choices = MONTHS.items()
|
||||
month_choices.sort()
|
||||
select_html = Select(choices=month_choices).render(self.month_field % name, month_val)
|
||||
output.append(select_html)
|
||||
|
||||
day_choices = [(i, i) for i in range(1, 32)]
|
||||
select_html = Select(choices=day_choices).render(self.day_field % name, day_val)
|
||||
output.append(select_html)
|
||||
|
||||
year_choices = [(i, i) for i in self.years]
|
||||
select_html = Select(choices=year_choices).render(self.year_field % name, year_val)
|
||||
output.append(select_html)
|
||||
|
||||
return u'\n'.join(output)
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name)
|
||||
if y and m and d:
|
||||
return '%s-%s-%s' % (y, m, d)
|
||||
return None
|
@ -3,8 +3,8 @@ Field classes
|
||||
"""
|
||||
|
||||
from django.utils.translation import gettext
|
||||
from util import ValidationError, smart_unicode
|
||||
from widgets import TextInput, CheckboxInput, Select, SelectMultiple
|
||||
from util import ErrorList, ValidationError, smart_unicode
|
||||
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
|
||||
import datetime
|
||||
import re
|
||||
import time
|
||||
@ -12,10 +12,12 @@ import time
|
||||
__all__ = (
|
||||
'Field', 'CharField', 'IntegerField',
|
||||
'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
|
||||
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
|
||||
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
|
||||
'RegexField', 'EmailField', 'URLField', 'BooleanField',
|
||||
'ChoiceField', 'MultipleChoiceField',
|
||||
'ComboField',
|
||||
'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
|
||||
'ComboField', 'MultiValueField',
|
||||
'SplitDateTimeField',
|
||||
)
|
||||
|
||||
# These values, if given to to_python(), will trigger the self.required check.
|
||||
@ -28,15 +30,37 @@ except NameError:
|
||||
|
||||
class Field(object):
|
||||
widget = TextInput # Default widget to use when rendering this type of Field.
|
||||
hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
|
||||
|
||||
# Tracks each time a Field instance is created. Used to retain order.
|
||||
creation_counter = 0
|
||||
|
||||
def __init__(self, required=True, widget=None):
|
||||
self.required = required
|
||||
def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None):
|
||||
# required -- Boolean that specifies whether the field is required.
|
||||
# True by default.
|
||||
# widget -- A Widget class, or instance of a Widget class, that should be
|
||||
# used for this Field when displaying it. Each Field has a default
|
||||
# Widget that it'll use if you don't specify this. In most cases,
|
||||
# the default widget is TextInput.
|
||||
# label -- A verbose name for this field, for use in displaying this field in
|
||||
# a form. By default, Django will use a "pretty" version of the form
|
||||
# field name, if the Field is part of a Form.
|
||||
# initial -- A value to use in this Field's initial display. This value is
|
||||
# *not* used as a fallback if data isn't given.
|
||||
# help_text -- An optional string to use as "help text" for this Field.
|
||||
if label is not None:
|
||||
label = smart_unicode(label)
|
||||
self.required, self.label, self.initial = required, label, initial
|
||||
self.help_text = help_text
|
||||
widget = widget or self.widget
|
||||
if isinstance(widget, type):
|
||||
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
|
||||
|
||||
# Increase the creation counter, and save our local copy.
|
||||
@ -54,15 +78,24 @@ class Field(object):
|
||||
raise ValidationError(gettext(u'This field is required.'))
|
||||
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):
|
||||
def __init__(self, max_length=None, min_length=None, required=True, widget=None):
|
||||
Field.__init__(self, required, widget)
|
||||
def __init__(self, max_length=None, min_length=None, *args, **kwargs):
|
||||
self.max_length, self.min_length = max_length, min_length
|
||||
super(CharField, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
"Validates max_length and min_length. Returns a Unicode object."
|
||||
Field.clean(self, value)
|
||||
if value in EMPTY_VALUES: value = u''
|
||||
super(CharField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return u''
|
||||
value = smart_unicode(value)
|
||||
if self.max_length is not None and len(value) > self.max_length:
|
||||
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||
@ -70,19 +103,32 @@ class CharField(Field):
|
||||
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
|
||||
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):
|
||||
def __init__(self, max_value=None, min_value=None, *args, **kwargs):
|
||||
self.max_value, self.min_value = max_value, min_value
|
||||
super(IntegerField, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Validates that int() can be called on the input. Returns the result
|
||||
of int().
|
||||
of int(). Returns None for empty values.
|
||||
"""
|
||||
super(IntegerField, self).clean(value)
|
||||
if not self.required and value in EMPTY_VALUES:
|
||||
return u''
|
||||
if value in EMPTY_VALUES:
|
||||
return None
|
||||
try:
|
||||
return int(value)
|
||||
value = int(value)
|
||||
except (ValueError, TypeError):
|
||||
raise ValidationError(gettext(u'Enter a whole number.'))
|
||||
if self.max_value is not None and value > self.max_value:
|
||||
raise ValidationError(gettext(u'Ensure this value is less than or equal to %s.') % self.max_value)
|
||||
if self.min_value is not None and value < self.min_value:
|
||||
raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value)
|
||||
return value
|
||||
|
||||
DEFAULT_DATE_INPUT_FORMATS = (
|
||||
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
|
||||
@ -93,8 +139,8 @@ DEFAULT_DATE_INPUT_FORMATS = (
|
||||
)
|
||||
|
||||
class DateField(Field):
|
||||
def __init__(self, input_formats=None, required=True, widget=None):
|
||||
Field.__init__(self, required, widget)
|
||||
def __init__(self, input_formats=None, *args, **kwargs):
|
||||
super(DateField, self).__init__(*args, **kwargs)
|
||||
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
||||
|
||||
def clean(self, value):
|
||||
@ -102,7 +148,7 @@ class DateField(Field):
|
||||
Validates that the input can be converted to a date. Returns a Python
|
||||
datetime.date object.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
super(DateField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return None
|
||||
if isinstance(value, datetime.datetime):
|
||||
@ -116,6 +162,33 @@ class DateField(Field):
|
||||
continue
|
||||
raise ValidationError(gettext(u'Enter a valid date.'))
|
||||
|
||||
DEFAULT_TIME_INPUT_FORMATS = (
|
||||
'%H:%M:%S', # '14:30:59'
|
||||
'%H:%M', # '14:30'
|
||||
)
|
||||
|
||||
class TimeField(Field):
|
||||
def __init__(self, input_formats=None, *args, **kwargs):
|
||||
super(TimeField, self).__init__(*args, **kwargs)
|
||||
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Validates that the input can be converted to a time. Returns a Python
|
||||
datetime.time object.
|
||||
"""
|
||||
super(TimeField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return None
|
||||
if isinstance(value, datetime.time):
|
||||
return value
|
||||
for format in self.input_formats:
|
||||
try:
|
||||
return datetime.time(*time.strptime(value, format)[3:6])
|
||||
except ValueError:
|
||||
continue
|
||||
raise ValidationError(gettext(u'Enter a valid time.'))
|
||||
|
||||
DEFAULT_DATETIME_INPUT_FORMATS = (
|
||||
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
|
||||
'%Y-%m-%d %H:%M', # '2006-10-25 14:30'
|
||||
@ -129,8 +202,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
|
||||
)
|
||||
|
||||
class DateTimeField(Field):
|
||||
def __init__(self, input_formats=None, required=True, widget=None):
|
||||
Field.__init__(self, required, widget)
|
||||
def __init__(self, input_formats=None, *args, **kwargs):
|
||||
super(DateTimeField, self).__init__(*args, **kwargs)
|
||||
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
||||
|
||||
def clean(self, value):
|
||||
@ -138,7 +211,7 @@ class DateTimeField(Field):
|
||||
Validates that the input can be converted to a datetime. Returns a
|
||||
Python datetime.datetime object.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
super(DateTimeField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return None
|
||||
if isinstance(value, datetime.datetime):
|
||||
@ -153,16 +226,17 @@ class DateTimeField(Field):
|
||||
raise ValidationError(gettext(u'Enter a valid date/time.'))
|
||||
|
||||
class RegexField(Field):
|
||||
def __init__(self, regex, error_message=None, required=True, widget=None):
|
||||
def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
|
||||
"""
|
||||
regex can be either a string or a compiled regular expression object.
|
||||
error_message is an optional error message to use, if
|
||||
'Enter a valid value' is too generic for you.
|
||||
"""
|
||||
Field.__init__(self, required, widget)
|
||||
super(RegexField, self).__init__(*args, **kwargs)
|
||||
if isinstance(regex, basestring):
|
||||
regex = re.compile(regex)
|
||||
self.regex = regex
|
||||
self.max_length, self.min_length = max_length, min_length
|
||||
self.error_message = error_message or gettext(u'Enter a valid value.')
|
||||
|
||||
def clean(self, value):
|
||||
@ -170,11 +244,16 @@ class RegexField(Field):
|
||||
Validates that the input matches the regular expression. Returns a
|
||||
Unicode object.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
if value in EMPTY_VALUES: value = u''
|
||||
super(RegexField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
value = u''
|
||||
value = smart_unicode(value)
|
||||
if not self.required and value == u'':
|
||||
if value == u'':
|
||||
return value
|
||||
if self.max_length is not None and len(value) > self.max_length:
|
||||
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||
if self.min_length is not None and len(value) < self.min_length:
|
||||
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
|
||||
if not self.regex.search(value):
|
||||
raise ValidationError(self.error_message)
|
||||
return value
|
||||
@ -185,8 +264,9 @@ email_re = re.compile(
|
||||
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
|
||||
|
||||
class EmailField(RegexField):
|
||||
def __init__(self, required=True, widget=None):
|
||||
RegexField.__init__(self, email_re, gettext(u'Enter a valid e-mail address.'), required, widget)
|
||||
def __init__(self, max_length=None, min_length=None, *args, **kwargs):
|
||||
RegexField.__init__(self, email_re, max_length, min_length,
|
||||
gettext(u'Enter a valid e-mail address.'), *args, **kwargs)
|
||||
|
||||
url_re = re.compile(
|
||||
r'^https?://' # http:// or https://
|
||||
@ -202,14 +282,16 @@ except ImportError:
|
||||
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
||||
|
||||
class URLField(RegexField):
|
||||
def __init__(self, required=True, verify_exists=False, widget=None,
|
||||
validator_user_agent=URL_VALIDATOR_USER_AGENT):
|
||||
RegexField.__init__(self, url_re, gettext(u'Enter a valid URL.'), required, widget)
|
||||
def __init__(self, max_length=None, min_length=None, verify_exists=False,
|
||||
validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
|
||||
super(URLField, self).__init__(url_re, max_length, min_length, gettext(u'Enter a valid URL.'), *args, **kwargs)
|
||||
self.verify_exists = verify_exists
|
||||
self.user_agent = validator_user_agent
|
||||
|
||||
def clean(self, value):
|
||||
value = RegexField.clean(self, value)
|
||||
value = super(URLField, self).clean(value)
|
||||
if value == u'':
|
||||
return value
|
||||
if self.verify_exists:
|
||||
import urllib2
|
||||
from django.conf import settings
|
||||
@ -234,24 +316,43 @@ class BooleanField(Field):
|
||||
|
||||
def clean(self, value):
|
||||
"Returns a Python boolean object."
|
||||
Field.clean(self, value)
|
||||
super(BooleanField, self).clean(value)
|
||||
return bool(value)
|
||||
|
||||
class NullBooleanField(BooleanField):
|
||||
"""
|
||||
A field whose valid values are None, True and False. Invalid values are
|
||||
cleaned to None.
|
||||
"""
|
||||
widget = NullBooleanSelect
|
||||
|
||||
def clean(self, value):
|
||||
return {True: True, False: False}.get(value, None)
|
||||
|
||||
class ChoiceField(Field):
|
||||
def __init__(self, choices=(), required=True, widget=Select):
|
||||
if isinstance(widget, type):
|
||||
widget = widget(choices=choices)
|
||||
Field.__init__(self, required, widget)
|
||||
def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None, help_text=None):
|
||||
super(ChoiceField, self).__init__(required, widget, label, initial, help_text)
|
||||
self.choices = choices
|
||||
|
||||
def _get_choices(self):
|
||||
return self._choices
|
||||
|
||||
def _set_choices(self, value):
|
||||
# Setting choices also sets the choices on the widget.
|
||||
self._choices = value
|
||||
self.widget.choices = value
|
||||
|
||||
choices = property(_get_choices, _set_choices)
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Validates that the input is in self.choices.
|
||||
"""
|
||||
value = Field.clean(self, value)
|
||||
if value in EMPTY_VALUES: value = u''
|
||||
value = super(ChoiceField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
value = u''
|
||||
value = smart_unicode(value)
|
||||
if not self.required and value == u'':
|
||||
if value == u'':
|
||||
return value
|
||||
valid_values = set([str(k) for k, v in self.choices])
|
||||
if value not in valid_values:
|
||||
@ -259,8 +360,10 @@ class ChoiceField(Field):
|
||||
return value
|
||||
|
||||
class MultipleChoiceField(ChoiceField):
|
||||
def __init__(self, choices=(), required=True, widget=SelectMultiple):
|
||||
ChoiceField.__init__(self, choices, required, widget)
|
||||
hidden_widget = MultipleHiddenInput
|
||||
|
||||
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None, help_text=None):
|
||||
super(MultipleChoiceField, self).__init__(choices, required, widget, label, initial, help_text)
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
@ -284,8 +387,11 @@ class MultipleChoiceField(ChoiceField):
|
||||
return new_value
|
||||
|
||||
class ComboField(Field):
|
||||
def __init__(self, fields=(), required=True, widget=None):
|
||||
Field.__init__(self, required, widget)
|
||||
"""
|
||||
A Field whose clean() method calls multiple Field clean() methods.
|
||||
"""
|
||||
def __init__(self, fields=(), *args, **kwargs):
|
||||
super(ComboField, self).__init__(*args, **kwargs)
|
||||
# Set 'required' to False on the individual fields, because the
|
||||
# required validation will be handled by ComboField, not by those
|
||||
# individual fields.
|
||||
@ -298,7 +404,88 @@ class ComboField(Field):
|
||||
Validates the given value against all of self.fields, which is a
|
||||
list of Field instances.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
super(ComboField, self).clean(value)
|
||||
for field in self.fields:
|
||||
value = field.clean(value)
|
||||
return value
|
||||
|
||||
class MultiValueField(Field):
|
||||
"""
|
||||
A Field that is composed of multiple Fields.
|
||||
|
||||
Its clean() method takes a "decompressed" list of values. Each value in
|
||||
this list is cleaned by the corresponding field -- the first value is
|
||||
cleaned by the first field, the second value is cleaned by the second
|
||||
field, etc. Once all fields are cleaned, the list of clean values is
|
||||
"compressed" into a single value.
|
||||
|
||||
Subclasses should implement compress(), which specifies how a list of
|
||||
valid values should be converted to a single value. Subclasses should not
|
||||
have to implement clean().
|
||||
|
||||
You'll probably want to use this with MultiWidget.
|
||||
"""
|
||||
def __init__(self, fields=(), *args, **kwargs):
|
||||
super(MultiValueField, self).__init__(*args, **kwargs)
|
||||
# Set 'required' to False on the individual fields, because the
|
||||
# required validation will be handled by MultiValueField, not by those
|
||||
# individual fields.
|
||||
for f in fields:
|
||||
f.required = False
|
||||
self.fields = fields
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Validates every value in the given list. A value is validated against
|
||||
the corresponding Field in self.fields.
|
||||
|
||||
For example, if this MultiValueField was instantiated with
|
||||
fields=(DateField(), TimeField()), clean() would call
|
||||
DateField.clean(value[0]) and TimeField.clean(value[1]).
|
||||
"""
|
||||
clean_data = []
|
||||
errors = ErrorList()
|
||||
if self.required and not value:
|
||||
raise ValidationError(gettext(u'This field is required.'))
|
||||
elif not self.required and not value:
|
||||
return self.compress([])
|
||||
if not isinstance(value, (list, tuple)):
|
||||
raise ValidationError(gettext(u'Enter a list of values.'))
|
||||
for i, field in enumerate(self.fields):
|
||||
try:
|
||||
field_value = value[i]
|
||||
except KeyError:
|
||||
field_value = None
|
||||
if self.required and field_value in EMPTY_VALUES:
|
||||
raise ValidationError(gettext(u'This field is required.'))
|
||||
try:
|
||||
clean_data.append(field.clean(field_value))
|
||||
except ValidationError, e:
|
||||
# Collect all validation errors in a single list, which we'll
|
||||
# raise at the end of clean(), rather than raising a single
|
||||
# exception for the first error we encounter.
|
||||
errors.extend(e.messages)
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
return self.compress(clean_data)
|
||||
|
||||
def compress(self, data_list):
|
||||
"""
|
||||
Returns a single value for the given list of values. The values can be
|
||||
assumed to be valid.
|
||||
|
||||
For example, if this MultiValueField was instantiated with
|
||||
fields=(DateField(), TimeField()), this might return a datetime
|
||||
object created by combining the date and time in data_list.
|
||||
"""
|
||||
raise NotImplementedError('Subclasses must implement this method.')
|
||||
|
||||
class SplitDateTimeField(MultiValueField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
fields = (DateField(), TimeField())
|
||||
super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
|
||||
|
||||
def compress(self, data_list):
|
||||
if data_list:
|
||||
return datetime.datetime.combine(*data_list)
|
||||
return None
|
||||
|
@ -2,11 +2,13 @@
|
||||
Form classes
|
||||
"""
|
||||
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.utils.datastructures import SortedDict, MultiValueDict
|
||||
from django.utils.html import escape
|
||||
from fields import Field
|
||||
from widgets import TextInput, Textarea, HiddenInput
|
||||
from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError
|
||||
from widgets import TextInput, Textarea, HiddenInput, MultipleHiddenInput
|
||||
from util import flatatt, StrAndUnicode, ErrorDict, ErrorList, ValidationError
|
||||
|
||||
__all__ = ('BaseForm', 'Form')
|
||||
|
||||
NON_FIELD_ERRORS = '__all__'
|
||||
|
||||
@ -24,24 +26,35 @@ class SortedDictFromList(SortedDict):
|
||||
self.keyOrder = [d[0] for d in data]
|
||||
dict.__init__(self, dict(data))
|
||||
|
||||
def copy(self):
|
||||
return SortedDictFromList(self.items())
|
||||
|
||||
class DeclarativeFieldsMetaclass(type):
|
||||
"Metaclass that converts Field attributes to a dictionary called 'fields'."
|
||||
"Metaclass that converts Field attributes to a dictionary called 'base_fields'."
|
||||
def __new__(cls, name, bases, attrs):
|
||||
fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)]
|
||||
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
|
||||
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
|
||||
attrs['fields'] = SortedDictFromList(fields)
|
||||
attrs['base_fields'] = SortedDictFromList(fields)
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
|
||||
class Form(StrAndUnicode):
|
||||
"A collection of Fields, plus their associated data."
|
||||
__metaclass__ = DeclarativeFieldsMetaclass
|
||||
|
||||
def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
|
||||
self.ignore_errors = data is None
|
||||
class BaseForm(StrAndUnicode):
|
||||
# This is the main implementation of all the Form logic. Note that this
|
||||
# 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*
|
||||
# class, not to the Form class.
|
||||
def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):
|
||||
self.is_bound = data is not None
|
||||
self.data = data or {}
|
||||
self.auto_id = auto_id
|
||||
self.clean_data = None # Stores the data after clean() has been called.
|
||||
self.prefix = prefix
|
||||
self.initial = initial or {}
|
||||
self.__errors = None # Stores the errors after clean() has been called.
|
||||
# The base_fields class attribute is the *class-wide* definition of
|
||||
# fields. Because a particular *instance* of the class might want to
|
||||
# alter self.fields, we create self.fields here by copying base_fields.
|
||||
# Instances should always modify self.fields; they should not modify
|
||||
# self.base_fields.
|
||||
self.fields = self.base_fields.copy()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.as_table()
|
||||
@ -70,9 +83,18 @@ class Form(StrAndUnicode):
|
||||
Returns True if the form has no errors. Otherwise, False. If errors are
|
||||
being ignored, returns False.
|
||||
"""
|
||||
return not self.ignore_errors and not bool(self.errors)
|
||||
return self.is_bound and not bool(self.errors)
|
||||
|
||||
def _html_output(self, normal_row, error_row, row_ender, errors_on_separate_row):
|
||||
def add_prefix(self, field_name):
|
||||
"""
|
||||
Returns the field name with a prefix appended, if this Form has a
|
||||
prefix set.
|
||||
|
||||
Subclasses may wish to override.
|
||||
"""
|
||||
return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name
|
||||
|
||||
def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
|
||||
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
|
||||
top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
|
||||
output, hidden_fields = [], []
|
||||
@ -86,7 +108,12 @@ class Form(StrAndUnicode):
|
||||
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.verbose_name+':')), 'field': bf})
|
||||
label = bf.label and bf.label_tag(escape(bf.label + ':')) or ''
|
||||
if field.help_text:
|
||||
help_text = help_text_html % field.help_text
|
||||
else:
|
||||
help_text = u''
|
||||
output.append(normal_row % {'errors': bf_errors, 'label': label, 'field': unicode(bf), 'help_text': help_text})
|
||||
if top_errors:
|
||||
output.insert(0, error_row % top_errors)
|
||||
if hidden_fields: # Insert any hidden fields in the last row.
|
||||
@ -101,15 +128,15 @@ class Form(StrAndUnicode):
|
||||
|
||||
def as_table(self):
|
||||
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
|
||||
return self._html_output(u'<tr><td>%(label)s</td><td>%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', True)
|
||||
return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
|
||||
|
||||
def as_ul(self):
|
||||
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
|
||||
return self._html_output(u'<li>%(errors)s%(label)s %(field)s</li>', u'<li>%s</li>', '</li>', False)
|
||||
return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
|
||||
|
||||
def as_p(self):
|
||||
"Returns this form rendered as HTML <p>s."
|
||||
return self._html_output(u'<p>%(label)s %(field)s</p>', u'<p>%s</p>', '</p>', True)
|
||||
return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'<p>%s</p>', '</p>', u' %s', True)
|
||||
|
||||
def non_field_errors(self):
|
||||
"""
|
||||
@ -123,16 +150,16 @@ class Form(StrAndUnicode):
|
||||
"""
|
||||
Cleans all of self.data and populates self.__errors and self.clean_data.
|
||||
"""
|
||||
self.clean_data = {}
|
||||
errors = ErrorDict()
|
||||
if self.ignore_errors: # Stop further processing.
|
||||
if not self.is_bound: # Stop further processing.
|
||||
self.__errors = errors
|
||||
return
|
||||
self.clean_data = {}
|
||||
for name, field in self.fields.items():
|
||||
# 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, name)
|
||||
value = field.widget.value_from_datadict(self.data, self.add_prefix(name))
|
||||
try:
|
||||
value = field.clean(value)
|
||||
self.clean_data[name] = value
|
||||
@ -146,7 +173,7 @@ class Form(StrAndUnicode):
|
||||
except ValidationError, e:
|
||||
errors[NON_FIELD_ERRORS] = e.messages
|
||||
if errors:
|
||||
self.clean_data = None
|
||||
delattr(self, 'clean_data')
|
||||
self.__errors = errors
|
||||
|
||||
def clean(self):
|
||||
@ -158,12 +185,26 @@ class Form(StrAndUnicode):
|
||||
"""
|
||||
return self.clean_data
|
||||
|
||||
class Form(BaseForm):
|
||||
"A collection of Fields, plus their associated data."
|
||||
# This is a separate class from BaseForm in order to abstract the way
|
||||
# self.fields is specified. This class (Form) is the one that does the
|
||||
# fancy metaclass stuff purely for the semantic sugar -- it allows one
|
||||
# to define a form using declarative syntax.
|
||||
# BaseForm itself has no way of designating self.fields.
|
||||
__metaclass__ = DeclarativeFieldsMetaclass
|
||||
|
||||
class BoundField(StrAndUnicode):
|
||||
"A Field plus data"
|
||||
def __init__(self, form, field, name):
|
||||
self.form = form
|
||||
self.field = field
|
||||
self.name = name
|
||||
self.html_name = form.add_prefix(name)
|
||||
if self.field.label is None:
|
||||
self.label = pretty_name(name)
|
||||
else:
|
||||
self.label = self.field.label
|
||||
|
||||
def __unicode__(self):
|
||||
"Renders this field as an HTML widget."
|
||||
@ -190,7 +231,11 @@ class BoundField(StrAndUnicode):
|
||||
auto_id = self.auto_id
|
||||
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
|
||||
attrs['id'] = auto_id
|
||||
return widget.render(self.name, self.data, attrs=attrs)
|
||||
if not self.form.is_bound:
|
||||
data = self.form.initial.get(self.name, self.field.initial)
|
||||
else:
|
||||
data = self.data
|
||||
return widget.render(self.html_name, data, attrs=attrs)
|
||||
|
||||
def as_text(self, attrs=None):
|
||||
"""
|
||||
@ -206,28 +251,29 @@ class BoundField(StrAndUnicode):
|
||||
"""
|
||||
Returns a string of HTML for representing this as an <input type="hidden">.
|
||||
"""
|
||||
return self.as_widget(HiddenInput(), attrs)
|
||||
return self.as_widget(self.field.hidden_widget(), attrs)
|
||||
|
||||
def _data(self):
|
||||
"Returns the data for this BoundField, or None if it wasn't given."
|
||||
return self.form.data.get(self.name, None)
|
||||
"""
|
||||
Returns the data for this BoundField, or None if it wasn't given.
|
||||
"""
|
||||
return self.field.widget.value_from_datadict(self.form.data, self.html_name)
|
||||
data = property(_data)
|
||||
|
||||
def _verbose_name(self):
|
||||
return pretty_name(self.name)
|
||||
verbose_name = property(_verbose_name)
|
||||
|
||||
def label_tag(self, contents=None):
|
||||
def label_tag(self, contents=None, attrs=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 verbose_name.
|
||||
field's HTML-escaped label.
|
||||
|
||||
If attrs are given, they're used as HTML attributes on the <label> tag.
|
||||
"""
|
||||
contents = contents or escape(self.verbose_name)
|
||||
contents = contents or escape(self.label)
|
||||
widget = self.field.widget
|
||||
id_ = widget.attrs.get('id') or self.auto_id
|
||||
if id_:
|
||||
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents)
|
||||
attrs = attrs and flatatt(attrs) or ''
|
||||
contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents)
|
||||
return contents
|
||||
|
||||
def _is_hidden(self):
|
||||
@ -242,8 +288,8 @@ class BoundField(StrAndUnicode):
|
||||
"""
|
||||
auto_id = self.form.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:
|
||||
return self.name
|
||||
return self.html_name
|
||||
return ''
|
||||
auto_id = property(_auto_id)
|
||||
|
@ -1,13 +1,98 @@
|
||||
"""
|
||||
Helper functions for creating Forms from Django models and database field objects.
|
||||
Helper functions for creating Form classes from Django models
|
||||
and database field objects.
|
||||
"""
|
||||
|
||||
__all__ = ('form_for_model', 'form_for_fields')
|
||||
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
|
||||
|
||||
def form_for_model(model):
|
||||
"Returns a Form instance for the given Django model class."
|
||||
raise NotImplementedError
|
||||
__all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields')
|
||||
|
||||
def model_save(self, commit=True):
|
||||
"""
|
||||
Creates and returns model instance according to self.clean_data.
|
||||
|
||||
This method is created for any form_for_model Form.
|
||||
"""
|
||||
if self.errors:
|
||||
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
|
||||
return save_instance(self, self._model(), commit)
|
||||
|
||||
def save_instance(form, instance, commit=True):
|
||||
"""
|
||||
Saves bound Form ``form``'s clean_data into model instance ``instance``.
|
||||
|
||||
Assumes ``form`` has a field for every non-AutoField database field in
|
||||
``instance``. If commit=True, then the changes to ``instance`` will be
|
||||
saved to the database. Returns ``instance``.
|
||||
"""
|
||||
from django.db import models
|
||||
opts = instance.__class__._meta
|
||||
if form.errors:
|
||||
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
||||
clean_data = form.clean_data
|
||||
for f in opts.fields:
|
||||
if isinstance(f, models.AutoField):
|
||||
continue
|
||||
setattr(instance, f.attname, clean_data[f.name])
|
||||
if commit:
|
||||
instance.save()
|
||||
for f in opts.many_to_many:
|
||||
setattr(instance, f.attname, clean_data[f.name])
|
||||
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
|
||||
# data will be lost. This happens because a many-to-many options cannot be
|
||||
# set on an object until after it's saved. Maybe we should raise an
|
||||
# exception in that case.
|
||||
return instance
|
||||
|
||||
def make_instance_save(instance):
|
||||
"Returns the save() method for a form_for_instance Form."
|
||||
def save(self, commit=True):
|
||||
return save_instance(self, instance, commit)
|
||||
return save
|
||||
|
||||
def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield()):
|
||||
"""
|
||||
Returns a Form class for the given Django model class.
|
||||
|
||||
Provide ``form`` if you want to use a custom BaseForm subclass.
|
||||
|
||||
Provide ``formfield_callback`` if you want to define different logic for
|
||||
determining the formfield for a given database field. It's a callable that
|
||||
takes a database Field instance and returns a form Field instance.
|
||||
"""
|
||||
opts = model._meta
|
||||
field_list = []
|
||||
for f in opts.fields + opts.many_to_many:
|
||||
formfield = formfield_callback(f)
|
||||
if formfield:
|
||||
field_list.append((f.name, formfield))
|
||||
fields = SortedDictFromList(field_list)
|
||||
return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save})
|
||||
|
||||
def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
|
||||
"""
|
||||
Returns a Form class for the given Django model instance.
|
||||
|
||||
Provide ``form`` if you want to use a custom BaseForm subclass.
|
||||
|
||||
Provide ``formfield_callback`` if you want to define different logic for
|
||||
determining the formfield for a given database field. It's a callable that
|
||||
takes a database Field instance, plus **kwargs, and returns a form Field
|
||||
instance with the given kwargs (i.e. 'initial').
|
||||
"""
|
||||
model = instance.__class__
|
||||
opts = model._meta
|
||||
field_list = []
|
||||
for f in opts.fields + opts.many_to_many:
|
||||
current_value = f.value_from_object(instance)
|
||||
formfield = formfield_callback(f, initial=current_value)
|
||||
if formfield:
|
||||
field_list.append((f.name, formfield))
|
||||
fields = SortedDictFromList(field_list)
|
||||
return type(opts.object_name + 'InstanceForm', (form,),
|
||||
{'base_fields': fields, '_model': model, 'save': make_instance_save(instance)})
|
||||
|
||||
def form_for_fields(field_list):
|
||||
"Returns a Form instance for the given list of Django database field instances."
|
||||
raise NotImplementedError
|
||||
"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,), {'base_fields': fields})
|
||||
|
@ -1,4 +1,9 @@
|
||||
from django.conf import settings
|
||||
from django.utils.html import escape
|
||||
|
||||
# Converts a dictionary to a single string with key="value", XML-style with
|
||||
# a leading space. Assumes keys do not need to be XML-escaped.
|
||||
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
||||
|
||||
def smart_unicode(s):
|
||||
if not isinstance(s, basestring):
|
||||
|
@ -3,13 +3,16 @@ HTML Widget classes
|
||||
"""
|
||||
|
||||
__all__ = (
|
||||
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
|
||||
'Textarea', 'CheckboxInput',
|
||||
'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
|
||||
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',
|
||||
'FileInput', 'Textarea', 'CheckboxInput',
|
||||
'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
|
||||
'MultiWidget', 'SplitDateTimeWidget',
|
||||
)
|
||||
|
||||
from util import StrAndUnicode, smart_unicode
|
||||
from util import flatatt, StrAndUnicode, smart_unicode
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import gettext
|
||||
from itertools import chain
|
||||
|
||||
try:
|
||||
@ -17,18 +20,19 @@ try:
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
# Converts a dictionary to a single string with key="value", XML-style with
|
||||
# a leading space. Assumes keys do not need to be XML-escaped.
|
||||
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
||||
|
||||
class Widget(object):
|
||||
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):
|
||||
self.attrs = attrs or {}
|
||||
|
||||
def render(self, name, value):
|
||||
def render(self, name, value, attrs=None):
|
||||
"""
|
||||
Returns this Widget rendered as HTML, as a Unicode string.
|
||||
|
||||
The 'value' given is not guaranteed to be valid input, so subclass
|
||||
implementations should program defensively.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||
@ -64,6 +68,7 @@ class Input(Widget):
|
||||
type='radio', which are special).
|
||||
"""
|
||||
input_type = None # Subclasses must define this.
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
if value is None: value = ''
|
||||
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
||||
@ -80,6 +85,26 @@ class HiddenInput(Input):
|
||||
input_type = 'hidden'
|
||||
is_hidden = True
|
||||
|
||||
class MultipleHiddenInput(HiddenInput):
|
||||
"""
|
||||
A widget that handles <input type="hidden"> for fields that have a list
|
||||
of values.
|
||||
"""
|
||||
def __init__(self, attrs=None, choices=()):
|
||||
# choices can be any iterable
|
||||
self.attrs = attrs or {}
|
||||
self.choices = choices
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
if value is None: value = []
|
||||
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
||||
return u'\n'.join([(u'<input%s />' % flatatt(dict(value=smart_unicode(v), **final_attrs))) for v in value])
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
if isinstance(data, MultiValueDict):
|
||||
return data.getlist(name)
|
||||
return data.get(name, None)
|
||||
|
||||
class FileInput(Input):
|
||||
input_type = 'file'
|
||||
|
||||
@ -111,9 +136,11 @@ class CheckboxInput(Widget):
|
||||
|
||||
class Select(Widget):
|
||||
def __init__(self, attrs=None, choices=()):
|
||||
# choices can be any iterable
|
||||
self.attrs = attrs or {}
|
||||
self.choices = choices
|
||||
# choices can be any iterable, but we may need to render this widget
|
||||
# multiple times. Thus, collapse it into a list so it can be consumed
|
||||
# more than once.
|
||||
self.choices = list(choices)
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
if value is None: value = ''
|
||||
@ -127,8 +154,26 @@ class Select(Widget):
|
||||
output.append(u'</select>')
|
||||
return u'\n'.join(output)
|
||||
|
||||
class NullBooleanSelect(Select):
|
||||
"""
|
||||
A Select Widget intended to be used with NullBooleanField.
|
||||
"""
|
||||
def __init__(self, attrs=None):
|
||||
choices = ((u'1', gettext('Unknown')), (u'2', gettext('Yes')), (u'3', gettext('No')))
|
||||
super(NullBooleanSelect, self).__init__(attrs, choices)
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
try:
|
||||
value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value]
|
||||
except KeyError:
|
||||
value = u'1'
|
||||
return super(NullBooleanSelect, self).render(name, value, attrs, choices)
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
value = data.get(name, None)
|
||||
return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
|
||||
|
||||
class SelectMultiple(Widget):
|
||||
requires_data_list = True
|
||||
def __init__(self, attrs=None, choices=()):
|
||||
# choices can be any iterable
|
||||
self.attrs = attrs or {}
|
||||
@ -146,19 +191,25 @@ class SelectMultiple(Widget):
|
||||
output.append(u'</select>')
|
||||
return u'\n'.join(output)
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
if isinstance(data, MultiValueDict):
|
||||
return data.getlist(name)
|
||||
return data.get(name, None)
|
||||
|
||||
class RadioInput(StrAndUnicode):
|
||||
"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.choice_value = smart_unicode(choice[0])
|
||||
self.choice_label = smart_unicode(choice[1])
|
||||
self.index = index
|
||||
|
||||
def __unicode__(self):
|
||||
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
||||
|
||||
def is_checked(self):
|
||||
return self.value == smart_unicode(self.choice_value)
|
||||
return self.value == self.choice_value
|
||||
|
||||
def tag(self):
|
||||
if self.attrs.has_key('id'):
|
||||
@ -178,6 +229,10 @@ class RadioFieldRenderer(StrAndUnicode):
|
||||
for i, choice in enumerate(self.choices):
|
||||
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
choice = self.choices[idx] # Let the IndexError propogate
|
||||
return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
|
||||
|
||||
def __unicode__(self):
|
||||
"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])
|
||||
@ -203,11 +258,16 @@ class RadioSelect(Select):
|
||||
class CheckboxSelectMultiple(SelectMultiple):
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
if value is None: value = []
|
||||
has_id = attrs and attrs.has_key('id')
|
||||
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):
|
||||
for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
|
||||
# If an ID attribute was given, add a numeric index as a suffix,
|
||||
# so that the checkboxes don't all have the same ID attribute.
|
||||
if has_id:
|
||||
final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
|
||||
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
|
||||
option_value = smart_unicode(option_value)
|
||||
rendered_cb = cb.render(name, option_value)
|
||||
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label))))
|
||||
@ -220,3 +280,66 @@ class CheckboxSelectMultiple(SelectMultiple):
|
||||
id_ += '_0'
|
||||
return id_
|
||||
id_for_label = classmethod(id_for_label)
|
||||
|
||||
class MultiWidget(Widget):
|
||||
"""
|
||||
A widget that is composed of multiple widgets.
|
||||
|
||||
Its render() method takes a "decompressed" list of values, not a single
|
||||
value. Each value in this list is rendered in the corresponding widget --
|
||||
the first value is rendered in the first widget, the second value is
|
||||
rendered in the second widget, etc.
|
||||
|
||||
Subclasses should implement decompress(), which specifies how a single
|
||||
value should be converted to a list of values. Subclasses should not
|
||||
have to implement clean().
|
||||
|
||||
Subclasses may implement format_output(), which takes the list of rendered
|
||||
widgets and returns HTML that formats them any way you'd like.
|
||||
|
||||
You'll probably want to use this with MultiValueField.
|
||||
"""
|
||||
def __init__(self, widgets, attrs=None):
|
||||
self.widgets = [isinstance(w, type) and w() or w for w in widgets]
|
||||
super(MultiWidget, self).__init__(attrs)
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
# value is a list of values, each corresponding to a widget
|
||||
# in self.widgets.
|
||||
if not isinstance(value, list):
|
||||
value = self.decompress(value)
|
||||
output = []
|
||||
for i, widget in enumerate(self.widgets):
|
||||
try:
|
||||
widget_value = value[i]
|
||||
except KeyError:
|
||||
widget_value = None
|
||||
output.append(widget.render(name + '_%s' % i, widget_value, attrs))
|
||||
return self.format_output(output)
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
return [data.get(name + '_%s' % i) for i in range(len(self.widgets))]
|
||||
|
||||
def format_output(self, rendered_widgets):
|
||||
return u''.join(rendered_widgets)
|
||||
|
||||
def decompress(self, value):
|
||||
"""
|
||||
Returns a list of decompressed values for the given compressed value.
|
||||
The given value can be assumed to be valid, but not necessarily
|
||||
non-empty.
|
||||
"""
|
||||
raise NotImplementedError('Subclasses must implement this method.')
|
||||
|
||||
class SplitDateTimeWidget(MultiWidget):
|
||||
"""
|
||||
A Widget that splits datetime input into two <input type="text"> boxes.
|
||||
"""
|
||||
def __init__(self, attrs=None):
|
||||
widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs))
|
||||
super(SplitDateTimeWidget, self).__init__(widgets, attrs)
|
||||
|
||||
def decompress(self, value):
|
||||
if value:
|
||||
return [value.date(), value.time()]
|
||||
return [None, None]
|
||||
|
1010
django/oldforms/__init__.py
Normal file
1010
django/oldforms/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,20 +4,29 @@
|
||||
|
||||
from django.template import loader
|
||||
from django.http import HttpResponse, Http404
|
||||
|
||||
from django.db.models.manager import Manager
|
||||
|
||||
def render_to_response(*args, **kwargs):
|
||||
return HttpResponse(loader.render_to_string(*args, **kwargs))
|
||||
load_and_render = render_to_response # For backwards compatibility.
|
||||
|
||||
def get_object_or_404(klass, *args, **kwargs):
|
||||
if isinstance(klass, Manager):
|
||||
manager = klass
|
||||
klass = manager.model
|
||||
else:
|
||||
manager = klass._default_manager
|
||||
try:
|
||||
return klass._default_manager.get(*args, **kwargs)
|
||||
return manager.get(*args, **kwargs)
|
||||
except klass.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
def get_list_or_404(klass, *args, **kwargs):
|
||||
obj_list = list(klass._default_manager.filter(*args, **kwargs))
|
||||
if isinstance(klass, Manager):
|
||||
manager = klass
|
||||
else:
|
||||
manager = klass._default_manager
|
||||
obj_list = list(manager.filter(*args, **kwargs))
|
||||
if not obj_list:
|
||||
raise Http404
|
||||
return obj_list
|
||||
|
@ -27,20 +27,38 @@ def fix_ampersands(value):
|
||||
from django.utils.html import fix_ampersands
|
||||
return fix_ampersands(value)
|
||||
|
||||
def floatformat(text):
|
||||
def floatformat(text, arg=-1):
|
||||
"""
|
||||
Displays a floating point number as 34.2 (with one decimal place) -- but
|
||||
only if there's a point to be displayed
|
||||
If called without an argument, displays a floating point
|
||||
number as 34.2 -- but only if there's a point to be displayed.
|
||||
With a positive numeric argument, it displays that many decimal places
|
||||
always.
|
||||
With a negative numeric argument, it will display that many decimal
|
||||
places -- but only if there's places to be displayed.
|
||||
Examples:
|
||||
num1 = 34.23234
|
||||
num2 = 34.00000
|
||||
num1|floatformat results in 34.2
|
||||
num2|floatformat is 34
|
||||
num1|floatformat:3 is 34.232
|
||||
num2|floatformat:3 is 34.000
|
||||
num1|floatformat:-3 is 34.232
|
||||
num2|floatformat:-3 is 34
|
||||
"""
|
||||
try:
|
||||
f = float(text)
|
||||
except ValueError:
|
||||
return ''
|
||||
try:
|
||||
d = int(arg)
|
||||
except ValueError:
|
||||
return str(f)
|
||||
m = f - int(f)
|
||||
if m:
|
||||
return '%.1f' % f
|
||||
else:
|
||||
if not m and d < 0:
|
||||
return '%d' % int(f)
|
||||
else:
|
||||
formatstr = '%%.%df' % abs(d)
|
||||
return formatstr % f
|
||||
|
||||
def linenumbers(value):
|
||||
"Displays text with line numbers"
|
||||
|
@ -70,7 +70,7 @@ class SortedDict(dict):
|
||||
return self.keyOrder[:]
|
||||
|
||||
def values(self):
|
||||
return [dict.__getitem__(self,k) for k in self.keyOrder]
|
||||
return [dict.__getitem__(self, k) for k in self.keyOrder]
|
||||
|
||||
def update(self, dict):
|
||||
for k, v in dict.items():
|
||||
@ -81,6 +81,10 @@ class SortedDict(dict):
|
||||
self.keyOrder.append(key)
|
||||
return dict.setdefault(self, key, default)
|
||||
|
||||
def value_for_index(self, index):
|
||||
"Returns the value of the item at the given zero-based index."
|
||||
return self[self.keyOrder[index]]
|
||||
|
||||
class MultiValueDictKeyError(KeyError):
|
||||
pass
|
||||
|
||||
|
@ -11,7 +11,7 @@ Usage:
|
||||
>>>
|
||||
"""
|
||||
|
||||
from django.utils.dates import MONTHS, MONTHS_AP, WEEKDAYS
|
||||
from django.utils.dates import MONTHS, MONTHS_3, MONTHS_AP, WEEKDAYS
|
||||
from django.utils.tzinfo import LocalTimezone
|
||||
from calendar import isleap, monthrange
|
||||
import re, time
|
||||
@ -147,7 +147,7 @@ class DateFormat(TimeFormat):
|
||||
|
||||
def M(self):
|
||||
"Month, textual, 3 letters; e.g. 'Jan'"
|
||||
return MONTHS[self.data.month][0:3]
|
||||
return MONTHS_3[self.data.month].title()
|
||||
|
||||
def n(self):
|
||||
"Month without leading zeros; i.e. '1' to '12'"
|
||||
|
@ -1,4 +1,4 @@
|
||||
simplejson 1.3
|
||||
simplejson 1.5
|
||||
Copyright (c) 2006 Bob Ippolito
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
|
@ -27,6 +27,21 @@ Encoding basic Python object hierarchies::
|
||||
>>> io.getvalue()
|
||||
'["streaming API"]'
|
||||
|
||||
Compact encoding::
|
||||
|
||||
>>> import simplejson
|
||||
>>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
|
||||
'[1,2,3,{"4":5,"6":7}]'
|
||||
|
||||
Pretty printing::
|
||||
|
||||
>>> import simplejson
|
||||
>>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
|
||||
{
|
||||
"4": 5,
|
||||
"6": 7
|
||||
}
|
||||
|
||||
Decoding JSON::
|
||||
|
||||
>>> import simplejson
|
||||
@ -68,10 +83,10 @@ Extending JSONEncoder::
|
||||
['[', '2.0', ', ', '1.0', ']']
|
||||
|
||||
|
||||
Note that the JSON produced by this module is a subset of YAML,
|
||||
so it may be used as a serializer for that as well.
|
||||
Note that the JSON produced by this module's default settings
|
||||
is a subset of YAML, so it may be used as a serializer for that as well.
|
||||
"""
|
||||
__version__ = '1.3'
|
||||
__version__ = '1.5'
|
||||
__all__ = [
|
||||
'dump', 'dumps', 'load', 'loads',
|
||||
'JSONDecoder', 'JSONEncoder',
|
||||
@ -81,7 +96,7 @@ from django.utils.simplejson.decoder import JSONDecoder
|
||||
from django.utils.simplejson.encoder import JSONEncoder
|
||||
|
||||
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
allow_nan=True, cls=None, **kw):
|
||||
allow_nan=True, cls=None, indent=None, **kw):
|
||||
"""
|
||||
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
|
||||
``.write()``-supporting file-like object).
|
||||
@ -105,6 +120,10 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
in strict compliance of the JSON specification, instead of using the
|
||||
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
||||
|
||||
If ``indent`` is a non-negative integer, then JSON array elements and object
|
||||
members will be pretty-printed with that indent level. An indent level
|
||||
of 0 will only insert newlines. ``None`` is the most compact representation.
|
||||
|
||||
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
||||
``.default()`` method to serialize additional types), specify it with
|
||||
the ``cls`` kwarg.
|
||||
@ -112,7 +131,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
if cls is None:
|
||||
cls = JSONEncoder
|
||||
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||
check_circular=check_circular, allow_nan=allow_nan,
|
||||
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
|
||||
**kw).iterencode(obj)
|
||||
# could accelerate with writelines in some versions of Python, at
|
||||
# a debuggability cost
|
||||
@ -120,7 +139,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
fp.write(chunk)
|
||||
|
||||
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
allow_nan=True, cls=None, **kw):
|
||||
allow_nan=True, cls=None, indent=None, separators=None, **kw):
|
||||
"""
|
||||
Serialize ``obj`` to a JSON formatted ``str``.
|
||||
|
||||
@ -141,14 +160,26 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
strict compliance of the JSON specification, instead of using the
|
||||
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
||||
|
||||
If ``indent`` is a non-negative integer, then JSON array elements and
|
||||
object members will be pretty-printed with that indent level. An indent
|
||||
level of 0 will only insert newlines. ``None`` is the most compact
|
||||
representation.
|
||||
|
||||
If ``separators`` is an ``(item_separator, dict_separator)`` tuple
|
||||
then it will be used instead of the default ``(', ', ': ')`` separators.
|
||||
``(',', ':')`` is the most compact JSON representation.
|
||||
|
||||
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
||||
``.default()`` method to serialize additional types), specify it with
|
||||
the ``cls`` kwarg.
|
||||
"""
|
||||
if cls is None:
|
||||
cls = JSONEncoder
|
||||
return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||
check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj)
|
||||
return cls(
|
||||
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
|
||||
separators=separators,
|
||||
**kw).encode(obj)
|
||||
|
||||
def load(fp, encoding=None, cls=None, object_hook=None, **kw):
|
||||
"""
|
||||
|
@ -127,6 +127,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
|
||||
raise ValueError(errmsg("Expecting property name", s, end))
|
||||
end += 1
|
||||
encoding = getattr(context, 'encoding', None)
|
||||
iterscan = JSONScanner.iterscan
|
||||
while True:
|
||||
key, end = scanstring(s, end, encoding)
|
||||
end = _w(s, end).end()
|
||||
@ -134,7 +135,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
|
||||
raise ValueError(errmsg("Expecting : delimiter", s, end))
|
||||
end = _w(s, end + 1).end()
|
||||
try:
|
||||
value, end = JSONScanner.iterscan(s, idx=end).next()
|
||||
value, end = iterscan(s, idx=end, context=context).next()
|
||||
except StopIteration:
|
||||
raise ValueError(errmsg("Expecting object", s, end))
|
||||
pairs[key] = value
|
||||
@ -164,9 +165,10 @@ def JSONArray(match, context, _w=WHITESPACE.match):
|
||||
nextchar = s[end:end + 1]
|
||||
if nextchar == ']':
|
||||
return values, end + 1
|
||||
iterscan = JSONScanner.iterscan
|
||||
while True:
|
||||
try:
|
||||
value, end = JSONScanner.iterscan(s, idx=end).next()
|
||||
value, end = iterscan(s, idx=end, context=context).next()
|
||||
except StopIteration:
|
||||
raise ValueError(errmsg("Expecting object", s, end))
|
||||
values.append(value)
|
||||
|
@ -3,11 +3,11 @@ Implementation of JSONEncoder
|
||||
"""
|
||||
import re
|
||||
|
||||
# this should match any kind of infinity
|
||||
INFCHARS = re.compile(r'[infINF]')
|
||||
ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
|
||||
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
|
||||
ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
|
||||
ESCAPE_DCT = {
|
||||
# escape all forward slashes to prevent </script> attack
|
||||
'/': '\\/',
|
||||
'\\': '\\\\',
|
||||
'"': '\\"',
|
||||
'\b': '\\b',
|
||||
@ -16,31 +16,31 @@ ESCAPE_DCT = {
|
||||
'\r': '\\r',
|
||||
'\t': '\\t',
|
||||
}
|
||||
for i in range(20):
|
||||
for i in range(0x20):
|
||||
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
|
||||
|
||||
# assume this produces an infinity on all machines (probably not guaranteed)
|
||||
INFINITY = float('1e66666')
|
||||
|
||||
def floatstr(o, allow_nan=True):
|
||||
s = str(o)
|
||||
# If the first non-sign is a digit then it's not a special value
|
||||
if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
|
||||
return s
|
||||
elif not allow_nan:
|
||||
# Check for specials. Note that this type of test is processor- and/or
|
||||
# platform-specific, so do tests which don't depend on the internals.
|
||||
|
||||
if o != o:
|
||||
text = 'NaN'
|
||||
elif o == INFINITY:
|
||||
text = 'Infinity'
|
||||
elif o == -INFINITY:
|
||||
text = '-Infinity'
|
||||
else:
|
||||
return str(o)
|
||||
|
||||
if not allow_nan:
|
||||
raise ValueError("Out of range float values are not JSON compliant: %r"
|
||||
% (o,))
|
||||
# These are the string representations on the platforms I've tried
|
||||
if s == 'nan':
|
||||
return 'NaN'
|
||||
if s == 'inf':
|
||||
return 'Infinity'
|
||||
if s == '-inf':
|
||||
return '-Infinity'
|
||||
# NaN should either be inequal to itself, or equal to everything
|
||||
if o != o or o == 0.0:
|
||||
return 'NaN'
|
||||
# Last ditch effort, assume inf
|
||||
if o < 0:
|
||||
return '-Infinity'
|
||||
return 'Infinity'
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def encode_basestring(s):
|
||||
"""
|
||||
@ -90,8 +90,11 @@ class JSONEncoder(object):
|
||||
implementation (to raise ``TypeError``).
|
||||
"""
|
||||
__all__ = ['__init__', 'default', 'encode', 'iterencode']
|
||||
item_separator = ', '
|
||||
key_separator = ': '
|
||||
def __init__(self, skipkeys=False, ensure_ascii=True,
|
||||
check_circular=True, allow_nan=True, sort_keys=False):
|
||||
check_circular=True, allow_nan=True, sort_keys=False,
|
||||
indent=None, separators=None):
|
||||
"""
|
||||
Constructor for JSONEncoder, with sensible defaults.
|
||||
|
||||
@ -116,6 +119,15 @@ class JSONEncoder(object):
|
||||
If sort_keys is True, then the output of dictionaries will be
|
||||
sorted by key; this is useful for regression tests to ensure
|
||||
that JSON serializations can be compared on a day-to-day basis.
|
||||
|
||||
If indent is a non-negative integer, then JSON array
|
||||
elements and object members will be pretty-printed with that
|
||||
indent level. An indent level of 0 will only insert newlines.
|
||||
None is the most compact representation.
|
||||
|
||||
If specified, separators should be a (item_separator, key_separator)
|
||||
tuple. The default is (', ', ': '). To get the most compact JSON
|
||||
representation you should specify (',', ':') to eliminate whitespace.
|
||||
"""
|
||||
|
||||
self.skipkeys = skipkeys
|
||||
@ -123,6 +135,13 @@ class JSONEncoder(object):
|
||||
self.check_circular = check_circular
|
||||
self.allow_nan = allow_nan
|
||||
self.sort_keys = sort_keys
|
||||
self.indent = indent
|
||||
self.current_indent_level = 0
|
||||
if separators is not None:
|
||||
self.item_separator, self.key_separator = separators
|
||||
|
||||
def _newline_indent(self):
|
||||
return '\n' + (' ' * (self.indent * self.current_indent_level))
|
||||
|
||||
def _iterencode_list(self, lst, markers=None):
|
||||
if not lst:
|
||||
@ -134,14 +153,25 @@ class JSONEncoder(object):
|
||||
raise ValueError("Circular reference detected")
|
||||
markers[markerid] = lst
|
||||
yield '['
|
||||
if self.indent is not None:
|
||||
self.current_indent_level += 1
|
||||
newline_indent = self._newline_indent()
|
||||
separator = self.item_separator + newline_indent
|
||||
yield newline_indent
|
||||
else:
|
||||
newline_indent = None
|
||||
separator = self.item_separator
|
||||
first = True
|
||||
for value in lst:
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
yield ', '
|
||||
yield separator
|
||||
for chunk in self._iterencode(value, markers):
|
||||
yield chunk
|
||||
if newline_indent is not None:
|
||||
self.current_indent_level -= 1
|
||||
yield self._newline_indent()
|
||||
yield ']'
|
||||
if markers is not None:
|
||||
del markers[markerid]
|
||||
@ -156,6 +186,15 @@ class JSONEncoder(object):
|
||||
raise ValueError("Circular reference detected")
|
||||
markers[markerid] = dct
|
||||
yield '{'
|
||||
key_separator = self.key_separator
|
||||
if self.indent is not None:
|
||||
self.current_indent_level += 1
|
||||
newline_indent = self._newline_indent()
|
||||
item_separator = self.item_separator + newline_indent
|
||||
yield newline_indent
|
||||
else:
|
||||
newline_indent = None
|
||||
item_separator = self.item_separator
|
||||
first = True
|
||||
if self.ensure_ascii:
|
||||
encoder = encode_basestring_ascii
|
||||
@ -165,7 +204,7 @@ class JSONEncoder(object):
|
||||
if self.sort_keys:
|
||||
keys = dct.keys()
|
||||
keys.sort()
|
||||
items = [(k,dct[k]) for k in keys]
|
||||
items = [(k, dct[k]) for k in keys]
|
||||
else:
|
||||
items = dct.iteritems()
|
||||
for key, value in items:
|
||||
@ -190,11 +229,14 @@ class JSONEncoder(object):
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
yield ', '
|
||||
yield item_separator
|
||||
yield encoder(key)
|
||||
yield ': '
|
||||
yield key_separator
|
||||
for chunk in self._iterencode(value, markers):
|
||||
yield chunk
|
||||
if newline_indent is not None:
|
||||
self.current_indent_level -= 1
|
||||
yield self._newline_indent()
|
||||
yield '}'
|
||||
if markers is not None:
|
||||
del markers[markerid]
|
||||
|
40
django/utils/simplejson/jsonfilter.py
Normal file
40
django/utils/simplejson/jsonfilter.py
Normal file
@ -0,0 +1,40 @@
|
||||
from django.utils import simplejson
|
||||
import cgi
|
||||
|
||||
class JSONFilter(object):
|
||||
def __init__(self, app, mime_type='text/x-json'):
|
||||
self.app = app
|
||||
self.mime_type = mime_type
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
# Read JSON POST input to jsonfilter.json if matching mime type
|
||||
response = {'status': '200 OK', 'headers': []}
|
||||
def json_start_response(status, headers):
|
||||
response['status'] = status
|
||||
response['headers'].extend(headers)
|
||||
environ['jsonfilter.mime_type'] = self.mime_type
|
||||
if environ.get('REQUEST_METHOD', '') == 'POST':
|
||||
if environ.get('CONTENT_TYPE', '') == self.mime_type:
|
||||
args = [_ for _ in [environ.get('CONTENT_LENGTH')] if _]
|
||||
data = environ['wsgi.input'].read(*map(int, args))
|
||||
environ['jsonfilter.json'] = simplejson.loads(data)
|
||||
res = simplejson.dumps(self.app(environ, json_start_response))
|
||||
jsonp = cgi.parse_qs(environ.get('QUERY_STRING', '')).get('jsonp')
|
||||
if jsonp:
|
||||
content_type = 'text/javascript'
|
||||
res = ''.join(jsonp + ['(', res, ')'])
|
||||
elif 'Opera' in environ.get('HTTP_USER_AGENT', ''):
|
||||
# Opera has bunk XMLHttpRequest support for most mime types
|
||||
content_type = 'text/plain'
|
||||
else:
|
||||
content_type = self.mime_type
|
||||
headers = [
|
||||
('Content-type', content_type),
|
||||
('Content-length', len(res)),
|
||||
]
|
||||
headers.extend(response['headers'])
|
||||
start_response(response['status'], headers)
|
||||
return [res]
|
||||
|
||||
def factory(app, global_conf, **kw):
|
||||
return JSONFilter(app, **kw)
|
@ -3,11 +3,12 @@ Iterator based sre token scanner
|
||||
"""
|
||||
import sre_parse, sre_compile, sre_constants
|
||||
from sre_constants import BRANCH, SUBPATTERN
|
||||
from re import VERBOSE, MULTILINE, DOTALL
|
||||
import re
|
||||
|
||||
__all__ = ['Scanner', 'pattern']
|
||||
|
||||
FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL)
|
||||
FLAGS = (VERBOSE | MULTILINE | DOTALL)
|
||||
class Scanner(object):
|
||||
def __init__(self, lexicon, flags=FLAGS):
|
||||
self.actions = [None]
|
||||
|
@ -8,17 +8,28 @@ capfirst = lambda x: x and x[0].upper() + x[1:]
|
||||
def wrap(text, width):
|
||||
"""
|
||||
A word-wrap function that preserves existing line breaks and most spaces in
|
||||
the text. Expects that existing line breaks are posix newlines (\n).
|
||||
See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
|
||||
the text. Expects that existing line breaks are posix newlines.
|
||||
"""
|
||||
return reduce(lambda line, word, width=width: '%s%s%s' %
|
||||
(line,
|
||||
' \n'[(len(line[line.rfind('\n')+1:])
|
||||
+ len(word.split('\n',1)[0]
|
||||
) >= width)],
|
||||
word),
|
||||
text.split(' ')
|
||||
)
|
||||
def _generator():
|
||||
it = iter(text.split(' '))
|
||||
word = it.next()
|
||||
yield word
|
||||
pos = len(word) - word.rfind('\n') - 1
|
||||
for word in it:
|
||||
if "\n" in word:
|
||||
lines = word.splitlines()
|
||||
else:
|
||||
lines = (word,)
|
||||
pos += len(lines[0]) + 1
|
||||
if pos > width:
|
||||
yield '\n'
|
||||
pos = len(lines[-1])
|
||||
else:
|
||||
yield ' '
|
||||
if len(lines) > 1:
|
||||
pos = len(lines[-1])
|
||||
yield word
|
||||
return "".join(_generator())
|
||||
|
||||
def truncate_words(s, num):
|
||||
"Truncates a string after a certain number of words."
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.core.xheaders import populate_xheaders
|
||||
from django.template import loader
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django.db.models import FileField
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
from django.template import RequestContext
|
||||
@ -56,7 +56,7 @@ def create_object(request, model, template_name=None,
|
||||
new_data = manipulator.flatten_data()
|
||||
|
||||
# Create the FormWrapper, template, context, response
|
||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
if not template_name:
|
||||
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
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.
|
||||
new_data = manipulator.flatten_data()
|
||||
|
||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
if not template_name:
|
||||
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
|
@ -17,7 +17,7 @@ admin
|
||||
The automatic Django administrative interface. For more information, see
|
||||
`Tutorial 2`_.
|
||||
|
||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
||||
.. _Tutorial 2: ../tutorial2/
|
||||
|
||||
auth
|
||||
====
|
||||
@ -26,7 +26,7 @@ Django's authentication framework.
|
||||
|
||||
See the `authentication documentation`_.
|
||||
|
||||
.. _authentication documentation: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _authentication documentation: ../authentication/
|
||||
|
||||
comments
|
||||
========
|
||||
@ -46,7 +46,7 @@ A middleware for preventing Cross Site Request Forgeries
|
||||
|
||||
See the `csrf documentation`_.
|
||||
|
||||
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/
|
||||
.. _csrf documentation: ../csrf/
|
||||
|
||||
formtools
|
||||
=========
|
||||
@ -137,7 +137,7 @@ A framework for managing simple "flat" HTML content in a database.
|
||||
|
||||
See the `flatpages documentation`_.
|
||||
|
||||
.. _flatpages documentation: http://www.djangoproject.com/documentation/flatpages/
|
||||
.. _flatpages documentation: ../flatpages/
|
||||
|
||||
markup
|
||||
======
|
||||
@ -157,7 +157,7 @@ A framework for managing redirects.
|
||||
|
||||
See the `redirects documentation`_.
|
||||
|
||||
.. _redirects documentation: http://www.djangoproject.com/documentation/redirects/
|
||||
.. _redirects documentation: ../redirects/
|
||||
|
||||
sites
|
||||
=====
|
||||
@ -168,7 +168,7 @@ one or more sites.
|
||||
|
||||
See the `sites documentation`_.
|
||||
|
||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
||||
.. _sites documentation: ../sites/
|
||||
|
||||
sitemaps
|
||||
========
|
||||
@ -177,7 +177,7 @@ A framework for generating Google sitemap XML files.
|
||||
|
||||
See the `sitemaps documentation`_.
|
||||
|
||||
.. _sitemaps documentation: http://www.djangoproject.com/documentation/sitemaps/
|
||||
.. _sitemaps documentation: ../sitemaps/
|
||||
|
||||
syndication
|
||||
===========
|
||||
@ -186,7 +186,7 @@ A framework for generating syndication feeds, in RSS and Atom, quite easily.
|
||||
|
||||
See the `syndication documentation`_.
|
||||
|
||||
.. _syndication documentation: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _syndication documentation: ../syndication/
|
||||
|
||||
Other add-ons
|
||||
=============
|
||||
|
@ -65,7 +65,7 @@ are equivalent::
|
||||
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
|
||||
PythonOption DJANGO_SETTINGS_MODULE mysite.settings
|
||||
|
||||
.. _authentication system: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _authentication system: ../authentication/
|
||||
.. _Subversion: http://subversion.tigris.org/
|
||||
.. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html
|
||||
.. _custom permissions: http://www.djangoproject.com/documentation/authentication/#custom-permissions
|
||||
.. _custom permissions: ../authentication/#custom-permissions
|
||||
|
@ -98,26 +98,26 @@ change:
|
||||
rewrite before Django 1.0. Even if the change isn't quite that drastic,
|
||||
there will at least be moderate changes.
|
||||
|
||||
.. _caching: http://www.djangoproject.com/documentation/cache/
|
||||
.. _custom template tags and libraries: http://www.djangoproject.com/documentation/templates_python/
|
||||
.. _database lookup: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _django-admin utility: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _fastcgi integration: http://www.djangoproject.com/documentation/fastcgi/
|
||||
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
|
||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _internationalization: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _legacy database integration: http://www.djangoproject.com/documentation/legacy_databases/
|
||||
.. _model definition: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _mod_python integration: http://www.djangoproject.com/documentation/modpython/
|
||||
.. _redirects: http://www.djangoproject.com/documentation/redirects/
|
||||
.. _request/response objects: http://www.djangoproject.com/documentation/request_response/
|
||||
.. _sending email: http://www.djangoproject.com/documentation/email/
|
||||
.. _sessions: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _settings: http://www.djangoproject.com/documentation/settings/
|
||||
.. _syndication: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _template language: http://www.djangoproject.com/documentation/templates/
|
||||
.. _transactions: http://www.djangoproject.com/documentation/transactions/
|
||||
.. _url dispatch: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _forms and validation: http://www.djangoproject.com/documentation/forms/
|
||||
.. _serialization: http://www.djangoproject.com/documentation/serialization/
|
||||
.. _authentication: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _caching: ../cache/
|
||||
.. _custom template tags and libraries: ../templates_python/
|
||||
.. _database lookup: ../db_api/
|
||||
.. _django-admin utility: ../django_admin/
|
||||
.. _fastcgi integration: ../fastcgi/
|
||||
.. _flatpages: ../flatpages/
|
||||
.. _generic views: ../generic_views/
|
||||
.. _internationalization: ../i18n/
|
||||
.. _legacy database integration: ../legacy_databases/
|
||||
.. _model definition: ../model_api/
|
||||
.. _mod_python integration: ../modpython/
|
||||
.. _redirects: ../redirects/
|
||||
.. _request/response objects: ../request_response/
|
||||
.. _sending email: ../email/
|
||||
.. _sessions: ../sessions/
|
||||
.. _settings: ../settings/
|
||||
.. _syndication: ../syndication/
|
||||
.. _template language: ../templates/
|
||||
.. _transactions: ../transactions/
|
||||
.. _url dispatch: ../url_dispatch/
|
||||
.. _forms and validation: ../forms/
|
||||
.. _serialization: ../serialization/
|
||||
.. _authentication: ../authentication/
|
||||
|
@ -144,8 +144,8 @@ custom methods:
|
||||
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
|
||||
doesn't allow profiles.
|
||||
|
||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _DEFAULT_FROM_EMAIL: http://www.djangoproject.com/documentation/settings/#default-from-email
|
||||
.. _Django model: ../model_api/
|
||||
.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
|
||||
|
||||
Manager functions
|
||||
~~~~~~~~~~~~~~~~~
|
||||
@ -271,8 +271,8 @@ previous section). You can tell them apart with ``is_authenticated()``, like so:
|
||||
else:
|
||||
# Do something for anonymous users.
|
||||
|
||||
.. _request objects: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
||||
.. _session documentation: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _request objects: ../request_response/#httprequest-objects
|
||||
.. _session documentation: ../sessions/
|
||||
|
||||
How to log a user in
|
||||
--------------------
|
||||
@ -441,8 +441,8 @@ block::
|
||||
|
||||
{% endblock %}
|
||||
|
||||
.. _forms documentation: http://www.djangoproject.com/documentation/forms/
|
||||
.. _site framework docs: http://www.djangoproject.com/documentation/sites/
|
||||
.. _forms documentation: ../forms/
|
||||
.. _site framework docs: ../sites/
|
||||
|
||||
Limiting access to logged-in users that pass a test
|
||||
---------------------------------------------------
|
||||
@ -544,7 +544,7 @@ For example::
|
||||
def limited_object_detail(*args, **kwargs):
|
||||
return object_detail(*args, **kwargs)
|
||||
|
||||
.. _generic view: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _generic view: ../generic_views/
|
||||
|
||||
Permissions
|
||||
===========
|
||||
@ -606,7 +606,7 @@ This example model creates three custom permissions::
|
||||
The only thing this does is create those extra permissions when you run
|
||||
``syncdb``.
|
||||
|
||||
.. _model Meta attribute: http://www.djangoproject.com/documentation/model_api/#meta-options
|
||||
.. _model Meta attribute: ../model_api/#meta-options
|
||||
|
||||
API reference
|
||||
-------------
|
||||
@ -645,7 +645,7 @@ The currently logged-in user and his/her permissions are made available in the
|
||||
setting contains ``"django.core.context_processors.auth"``, which is default.
|
||||
For more, see the `RequestContext docs`_.
|
||||
|
||||
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-requestcontext
|
||||
.. _RequestContext docs: ../templates_python/#subclassing-context-requestcontext
|
||||
|
||||
Users
|
||||
-----
|
||||
@ -691,7 +691,7 @@ Thus, you can check permissions in template ``{% if %}`` statements::
|
||||
<p>You don't have permission to do anything in the foo app.</p>
|
||||
{% endif %}
|
||||
|
||||
.. _template context: http://www.djangoproject.com/documentation/templates_python/
|
||||
.. _template context: ../templates_python/
|
||||
|
||||
Groups
|
||||
======
|
||||
@ -756,7 +756,7 @@ scenes, so any messages will be deleted even if you don't display them.
|
||||
Finally, note that this messages framework only works with users in the user
|
||||
database. To send messages to anonymous users, use the `session framework`_.
|
||||
|
||||
.. _session framework: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _session framework: ../sessions/
|
||||
|
||||
Other authentication sources
|
||||
============================
|
||||
|
@ -250,7 +250,7 @@ Additionally, ``CacheMiddleware`` automatically sets a few headers in each
|
||||
|
||||
See the `middleware documentation`_ for more on middleware.
|
||||
|
||||
.. _`middleware documentation`: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _`middleware documentation`: ../middleware/
|
||||
|
||||
The per-view cache
|
||||
==================
|
||||
|
@ -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
|
||||
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
|
||||
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
|
||||
@ -44,8 +47,10 @@ particular:
|
||||
|
||||
* **Do** write complete, reproducible, specific bug reports. Include as
|
||||
much information as you possibly can, complete with code snippets, test
|
||||
cases, etc. A minimal example that illustrates the bug in a nice small
|
||||
test case is the best possible bug report.
|
||||
cases, etc. This means including a clear, concise description of the
|
||||
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
|
||||
`django-users`_ list, or the `#django`_ IRC channel for that.
|
||||
@ -117,9 +122,99 @@ Patch style
|
||||
* Name the patch file with a ``.diff`` extension; this will let the ticket
|
||||
tracker apply correct syntax highlighting, which is quite helpful.
|
||||
|
||||
* Put the prefix "[patch] " before the title of your ticket. This will make
|
||||
it obvious that the ticket includes a patch, and it will add the ticket
|
||||
to the `list of tickets with patches`_.
|
||||
* Check the "Has patch" box on the ticket details. This will make it
|
||||
obvious that the ticket includes a patch, and it will add the ticket 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. A couple of dedicated volunteers work on this regularly, but more help
|
||||
is always appreciated.
|
||||
|
||||
Most of the workflow is based around the concept of a ticket's "triage stage".
|
||||
This stage describes where in its lifetime a given ticket is at any time.
|
||||
Along with a handful of flags, this field easily tells us what and who each
|
||||
ticket is waiting on.
|
||||
|
||||
Since a picture is worth a thousand words, let's start there:
|
||||
|
||||
.. image:: http://media.djangoproject.com/img/doc/djangotickets.png
|
||||
:height: 451
|
||||
:width: 590
|
||||
:alt: Django's ticket workflow
|
||||
|
||||
We've got two roles here:
|
||||
|
||||
* Core developers: people with commit access who make the decisions and
|
||||
write the bulk of the code.
|
||||
|
||||
* Ticket triagers: community members who keep track of tickets, making
|
||||
sure the tickets are always categorized correctly.
|
||||
|
||||
Second, note the four triage stages:
|
||||
|
||||
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
|
||||
examine the ticket and move it along.
|
||||
|
||||
2. "Design decision needed" means "this concept requires a design
|
||||
decision," which should be discussed either in the ticket comments or on
|
||||
django-developers.
|
||||
|
||||
3. Once a ticket is ruled to be approved for fixing, it's moved into the
|
||||
"Accepted" stage. This stage is where all the real work gets done.
|
||||
|
||||
4. If a ticket has an associated patch (see below), a triager will review the
|
||||
patch. If the patch is complete, it'll be marked as "ready for checkin" so
|
||||
that a core developer knows to review and check in the patches.
|
||||
|
||||
The second part of this workflow involves a set of flags the describe what the
|
||||
ticket has or needs in order to be "ready for checkin":
|
||||
|
||||
"Has patch"
|
||||
The means the ticket has an associated patch_. These will be
|
||||
reviewed to see if the patch is "good".
|
||||
|
||||
"Needs documentation"
|
||||
This flag is used for tickets with patches that need associated
|
||||
documentation. Complete documentation of features is a prerequisite
|
||||
before we can check a fix into the codebase.
|
||||
|
||||
"Needs tests"
|
||||
This flags the patch as needing associated unit tests. Again, this is a
|
||||
required part of a valid patch.
|
||||
|
||||
"Patch needs improvement"
|
||||
This flag means that although the ticket *has* a patch, it's not quite
|
||||
ready for checkin. This could mean the patch no longer applies
|
||||
cleanly, or that the code doesn't live up to our standards.
|
||||
|
||||
.. _required details: `Reporting bugs`_
|
||||
.. _good patch: `Patch style`_
|
||||
.. _patch: `Submitting patches`_
|
||||
|
||||
Submitting and maintaining translations
|
||||
=======================================
|
||||
@ -137,7 +232,7 @@ translated, here's what to do:
|
||||
`i18n documentation`_.
|
||||
|
||||
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
|
||||
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _i18n documentation: ../i18n/
|
||||
|
||||
Coding style
|
||||
============
|
||||
@ -262,7 +357,7 @@ The Django tests all use the testing infrastructure that ships with Django for
|
||||
testing applications. See `Testing Django applications`_ for an explanation of
|
||||
how to write new tests.
|
||||
|
||||
.. _Testing Django applications: http://www.djangoproject.com/documentation/testing/
|
||||
.. _Testing Django applications: ../testing/
|
||||
|
||||
Running the unit tests
|
||||
----------------------
|
||||
@ -338,21 +433,63 @@ trunk more than once.
|
||||
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>/
|
||||
|
||||
Or, if you've got a working directory you'd like to switch to use a branch,
|
||||
you can use::
|
||||
...where ``<branch>`` is the branch's name. See the `list of branch names`_.
|
||||
|
||||
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>/
|
||||
|
||||
...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
|
||||
``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
|
||||
=================
|
||||
@ -457,8 +594,7 @@ requests for commit access are potential flame-war starters, and will be ignored
|
||||
.. _search the tracker: http://code.djangoproject.com/search
|
||||
.. _django-users: http://groups.google.com/group/django-users
|
||||
.. _`#django`: irc://irc.freenode.net/django
|
||||
.. _list of tickets with patches: http://code.djangoproject.com/report/12
|
||||
.. _list of tickets with patches: http://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority
|
||||
.. _PEP 8: http://www.python.org/peps/pep-0008.html
|
||||
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n
|
||||
.. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases
|
||||
|
@ -1,9 +1,9 @@
|
||||
=====================================
|
||||
Cross Site Request Forgery Protection
|
||||
Cross Site Request Forgery protection
|
||||
=====================================
|
||||
|
||||
The CsrfMiddleware class provides easy-to-use protection against
|
||||
`Cross Site Request Forgeries`_. This type of attack occurs when a malicious
|
||||
The CsrfMiddleware class provides easy-to-use protection against
|
||||
`Cross Site Request Forgeries`_. This type of attack occurs when a malicious
|
||||
web site creates a link or form button that is intended to perform some action
|
||||
on your web site, using the credentials of a logged-in user who is tricked
|
||||
into clicking on the link in their browser.
|
||||
@ -12,12 +12,12 @@ The first defense against CSRF attacks is to ensure that GET requests
|
||||
are side-effect free. POST requests can then be protected by adding this
|
||||
middleware into your list of installed middleware.
|
||||
|
||||
|
||||
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
|
||||
|
||||
How to use it
|
||||
=============
|
||||
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
|
||||
|
||||
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
|
||||
your list of middleware classes, ``MIDDLEWARE_CLASSES``. It needs to process
|
||||
the response after the SessionMiddleware, so must come before it in the
|
||||
list. It also must process the response before things like compression
|
||||
@ -25,16 +25,17 @@ happen to the response, so it must come after GZipMiddleware in the list.
|
||||
|
||||
How it works
|
||||
============
|
||||
|
||||
CsrfMiddleware does two things:
|
||||
|
||||
1. It modifies outgoing requests by adding a hidden form field to all
|
||||
'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is
|
||||
a hash of the session ID plus a secret. If there is no session ID set,
|
||||
this modification of the response isn't done, so there is very little
|
||||
1. It modifies outgoing requests by adding a hidden form field to all
|
||||
'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is
|
||||
a hash of the session ID plus a secret. If there is no session ID set,
|
||||
this modification of the response isn't done, so there is very little
|
||||
performance penalty for those requests that don't have a session.
|
||||
|
||||
2. On all incoming POST requests that have the session cookie set, it
|
||||
checks that the 'csrfmiddlewaretoken' is present and correct. If it
|
||||
2. On all incoming POST requests that have the session cookie set, it
|
||||
checks that the 'csrfmiddlewaretoken' is present and correct. If it
|
||||
isn't, the user will get a 403 error.
|
||||
|
||||
This ensures that only forms that have originated from your web site
|
||||
@ -43,26 +44,26 @@ can be used to POST data back.
|
||||
It deliberately only targets HTTP POST requests (and the corresponding
|
||||
POST forms). GET requests ought never to have side effects (if you are
|
||||
using HTTP GET and POST correctly), and so a CSRF attack with a GET
|
||||
request will always be harmless.
|
||||
request will always be harmless.
|
||||
|
||||
POST requests that are not accompanied by a session cookie are not protected,
|
||||
but they do not need to be protected, since the 'attacking' web site
|
||||
could make these kind of requests anyway.
|
||||
|
||||
The Content-Type is checked before modifying the response, and only
|
||||
The Content-Type is checked before modifying the response, and only
|
||||
pages that are served as 'text/html' or 'application/xml+xhtml'
|
||||
are modified.
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
CsrfMiddleware requires Django's session framework to work. If you have
|
||||
a custom authentication system that manually sets cookies and the like,
|
||||
it won't help you.
|
||||
|
||||
If your app creates HTML pages and forms in some unusual way, (e.g.
|
||||
it sends fragments of HTML in javascript document.write statements)
|
||||
you might bypass the filter that adds the hidden field to the form,
|
||||
If your app creates HTML pages and forms in some unusual way, (e.g.
|
||||
it sends fragments of HTML in javascript document.write statements)
|
||||
you might bypass the filter that adds the hidden field to the form,
|
||||
in which case form submission will always fail. It may still be possible
|
||||
to use the middleware, provided you can find some way to get the
|
||||
CSRF token and ensure that is included when your form is submitted.
|
||||
|
||||
CSRF token and ensure that is included when your form is submitted.
|
@ -143,9 +143,9 @@ or ``UPDATE`` SQL statements. Specifically, when you call ``save()``, Django
|
||||
follows this algorithm:
|
||||
|
||||
* If the object's primary key attribute is set to a value that evaluates to
|
||||
``False`` (such as ``None`` or the empty string), Django executes a
|
||||
``SELECT`` query to determine whether a record with the given primary key
|
||||
already exists.
|
||||
``True`` (i.e., a value other than ``None`` or the empty string), Django
|
||||
executes a ``SELECT`` query to determine whether a record with the given
|
||||
primary key already exists.
|
||||
* If the record with the given primary key does already exist, Django
|
||||
executes an ``UPDATE`` query.
|
||||
* If the object's primary key attribute is *not* set, or if it's set but a
|
||||
@ -525,6 +525,21 @@ Examples::
|
||||
[datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)]
|
||||
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
|
||||
[datetime.datetime(2005, 3, 20)]
|
||||
|
||||
``none()``
|
||||
~~~~~~~~~~
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Returns an ``EmptyQuerySet`` -- a ``QuerySet`` that always evaluates to
|
||||
an empty list. This can be used in cases where you know that you should
|
||||
return an empty result set and your caller is expecting a ``QuerySet``
|
||||
object (instead of returning an empty list, for example.)
|
||||
|
||||
Examples::
|
||||
|
||||
>>> Entry.objects.none()
|
||||
[]
|
||||
|
||||
``select_related()``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
@ -1704,6 +1719,46 @@ For every ``ImageField``, the object will have ``get_FOO_height()`` and
|
||||
``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This
|
||||
returns the height (or width) of the image, as an integer, in pixels.
|
||||
|
||||
Shortcuts
|
||||
=========
|
||||
|
||||
As you develop views, you will discover a number of common idioms in the
|
||||
way you use the database API. Django encodes some of these idioms as
|
||||
shortcuts that can be used to simplify the process of writing views.
|
||||
|
||||
get_object_or_404()
|
||||
-------------------
|
||||
|
||||
One common idiom to use ``get()`` and raise ``Http404`` if the
|
||||
object doesn't exist. This idiom is captured by ``get_object_or_404()``.
|
||||
This function takes a Django model as its first argument and an
|
||||
arbitrary number of keyword arguments, which it passes to the manager's
|
||||
``get()`` function. It raises ``Http404`` if the object doesn't
|
||||
exist. For example::
|
||||
|
||||
# Get the Entry with a primary key of 3
|
||||
e = get_object_or_404(Entry, pk=3)
|
||||
|
||||
When you provide a model to this shortcut function, the default manager
|
||||
is used to execute the underlying ``get()`` query. If you don't want to
|
||||
use the default manager, or you want to search a list of related objects,
|
||||
you can provide ``get_object_or_404()`` with a manager object, instead.
|
||||
For example::
|
||||
|
||||
# Get the author of blog instance `e` with a name of 'Fred'
|
||||
a = get_object_or_404(e.authors, name='Fred')
|
||||
|
||||
# Use a custom manager 'recent_entries' in the search for an
|
||||
# entry with a primary key of 3
|
||||
e = get_object_or_404(Entry.recent_entries, pk=3)
|
||||
|
||||
get_list_or_404()
|
||||
-----------------
|
||||
|
||||
``get_list_or_404`` behaves the same was as ``get_object_or_404()``
|
||||
-- except the it uses using ``filter()`` instead of ``get()``. It raises
|
||||
``Http404`` if the list is empty.
|
||||
|
||||
Falling back to raw SQL
|
||||
=======================
|
||||
|
||||
|
@ -186,7 +186,7 @@ code.
|
||||
|
||||
This is the philosophy behind `template inheritance`_.
|
||||
|
||||
.. _template inheritance: http://www.djangoproject.com/documentation/templates/#template-inheritance
|
||||
.. _template inheritance: ../templates/#template-inheritance
|
||||
|
||||
Be decoupled from HTML
|
||||
----------------------
|
||||
|
@ -53,7 +53,7 @@ Prints the admin-index template snippet for the given appnames.
|
||||
Use admin-index template snippets if you want to customize the look and feel of
|
||||
your admin's index page. See `Tutorial 2`_ for more information.
|
||||
|
||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
||||
.. _Tutorial 2: ../tutorial2/
|
||||
|
||||
createcachetable [tablename]
|
||||
----------------------------
|
||||
@ -61,7 +61,7 @@ createcachetable [tablename]
|
||||
Creates a cache table named ``tablename`` for use with the database cache
|
||||
backend. See the `cache documentation`_ for more information.
|
||||
|
||||
.. _cache documentation: http://www.djangoproject.com/documentation/cache/
|
||||
.. _cache documentation: ../cache/
|
||||
|
||||
dbshell
|
||||
-------
|
||||
@ -190,7 +190,7 @@ By default, the development server doesn't serve any static files for your site
|
||||
you want to configure Django to serve static media, read the `serving static files`_
|
||||
documentation.
|
||||
|
||||
.. _serving static files: http://www.djangoproject.com/documentation/static_files/
|
||||
.. _serving static files: ../static_files/
|
||||
|
||||
Turning off auto-reload
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -253,6 +253,8 @@ files are piped directly into the database after all of the models'
|
||||
table-creation statements have been executed. Use this SQL hook to populate
|
||||
tables with any necessary initial records, SQL functions or test data.
|
||||
|
||||
Note that the order in which the SQL files are processed is undefined.
|
||||
|
||||
sqlreset [appname appname ...]
|
||||
--------------------------------------
|
||||
|
||||
@ -380,7 +382,7 @@ Example usage::
|
||||
|
||||
django-admin.py syncdb --verbosity=2
|
||||
|
||||
Verbosity determines the amount of notification and debug information that
|
||||
Verbosity determines the amount of notification and debug information that
|
||||
will be printed to the console. '0' is no output, '1' is normal output,
|
||||
and `2` is verbose output.
|
||||
|
||||
|
@ -101,9 +101,9 @@ The "From:" header of the e-mail will be the value of the `SERVER_EMAIL setting`
|
||||
|
||||
This method exists for convenience and readability.
|
||||
|
||||
.. _ADMINS setting: http://www.djangoproject.com/documentation/settings/#admins
|
||||
.. _EMAIL_SUBJECT_PREFIX setting: http://www.djangoproject.com/documentation/settings/#email-subject-prefix
|
||||
.. _SERVER_EMAIL setting: http://www.djangoproject.com/documentation/settings/#server-email
|
||||
.. _ADMINS setting: ../settings/#admins
|
||||
.. _EMAIL_SUBJECT_PREFIX setting: ../settings/#email-subject-prefix
|
||||
.. _SERVER_EMAIL setting: ../settings/#server-email
|
||||
|
||||
mail_managers() function
|
||||
========================
|
||||
@ -114,7 +114,7 @@ Here's the definition::
|
||||
|
||||
mail_managers(subject, message, fail_silently=False)
|
||||
|
||||
.. _MANAGERS setting: http://www.djangoproject.com/documentation/settings/#managers
|
||||
.. _MANAGERS setting: ../settings/#managers
|
||||
|
||||
Examples
|
||||
========
|
||||
|
24
docs/faq.txt
24
docs/faq.txt
@ -63,7 +63,7 @@ at any level -- database servers, caching servers or Web/application servers.
|
||||
The framework cleanly separates components such as its database layer and
|
||||
application layer. And it ships with a simple-yet-powerful `cache framework`_.
|
||||
|
||||
.. _`cache framework`: http://www.djangoproject.com/documentation/cache/
|
||||
.. _`cache framework`: ../cache/
|
||||
|
||||
Who's behind this?
|
||||
------------------
|
||||
@ -191,7 +191,7 @@ Like we said: We're picky.
|
||||
|
||||
We've documented our philosophies on the `design philosophies page`_.
|
||||
|
||||
.. _design philosophies page: http://www.djangoproject.com/documentation/design_philosophies/
|
||||
.. _design philosophies page: ../design_philosophies/
|
||||
|
||||
Do you have any of those nifty "screencast" things?
|
||||
---------------------------------------------------
|
||||
@ -277,9 +277,9 @@ How do I get started?
|
||||
run into trouble.
|
||||
|
||||
.. _`Download the code`: http://www.djangoproject.com/download/
|
||||
.. _`installation guide`: http://www.djangoproject.com/documentation/install/
|
||||
.. _tutorial: http://www.djangoproject.com/documentation/tutorial1/
|
||||
.. _documentation: http://www.djangoproject.com/documentation/
|
||||
.. _`installation guide`: ../install/
|
||||
.. _tutorial: ../tutorial1/
|
||||
.. _documentation: ../
|
||||
.. _ask questions: http://www.djangoproject.com/community/
|
||||
|
||||
How do I fix the "install a later version of setuptools" error?
|
||||
@ -337,7 +337,7 @@ If you just want to play around and develop things on your local computer, use
|
||||
the development Web server that comes with Django. Things should Just Work.
|
||||
|
||||
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
||||
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/
|
||||
.. _How to use Django with FastCGI: ../fastcgi/
|
||||
.. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
||||
|
||||
How do I install mod_python on Windows?
|
||||
@ -464,7 +464,7 @@ Can I use Django with a pre-existing database?
|
||||
|
||||
Yes. See `Integrating with a legacy database`_.
|
||||
|
||||
.. _`Integrating with a legacy database`: http://www.djangoproject.com/documentation/legacy_databases/
|
||||
.. _`Integrating with a legacy database`: ../legacy_databases/
|
||||
|
||||
If I make changes to a model, how do I update the database?
|
||||
-----------------------------------------------------------
|
||||
@ -511,7 +511,7 @@ type, create an initial data file and put something like this in it::
|
||||
As explained in the `SQL initial data file`_ documentation, this SQL file can
|
||||
contain arbitrary SQL, so you can make any sorts of changes you need to make.
|
||||
|
||||
.. _SQL initial data file: http://www.djangoproject.com/documentation/model_api/#providing-initial-sql-data
|
||||
.. _SQL initial data file: ../model_api/#providing-initial-sql-data
|
||||
|
||||
Why is Django leaking memory?
|
||||
-----------------------------
|
||||
@ -592,7 +592,7 @@ My admin-site CSS and images showed up fine using the development server, but th
|
||||
See `serving the admin files`_ in the "How to use Django with mod_python"
|
||||
documentation.
|
||||
|
||||
.. _serving the admin files: http://www.djangoproject.com/documentation/modpython/#serving-the-admin-files
|
||||
.. _serving the admin files: ../modpython/#serving-the-admin-files
|
||||
|
||||
My "list_filter" contains a ManyToManyField, but the filter doesn't display.
|
||||
----------------------------------------------------------------------------
|
||||
@ -630,7 +630,7 @@ site is built using semantic HTML and plenty of CSS hooks, so any changes you'd
|
||||
like to make should be possible by editing the stylesheet. We've got a
|
||||
`guide to the CSS used in the admin`_ to get you started.
|
||||
|
||||
.. _`guide to the CSS used in the admin`: http://www.djangoproject.com/documentation/admin_css/
|
||||
.. _`guide to the CSS used in the admin`: ../admin_css/
|
||||
|
||||
How do I create users without having to edit password hashes?
|
||||
-------------------------------------------------------------
|
||||
@ -640,7 +640,7 @@ development version, where this problem was fixed on Aug. 4, 2006.
|
||||
|
||||
You can also use the Python API. See `creating users`_ for full info.
|
||||
|
||||
.. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users
|
||||
.. _creating users: ../authentication/#creating-users
|
||||
|
||||
Contributing code
|
||||
=================
|
||||
@ -651,7 +651,7 @@ How can I get started contributing code to Django?
|
||||
Thanks for asking! We've written an entire document devoted to this question.
|
||||
It's titled `Contributing to Django`_.
|
||||
|
||||
.. _Contributing to Django: http://www.djangoproject.com/documentation/contributing/
|
||||
.. _Contributing to Django: ../contributing/
|
||||
|
||||
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
@ -17,7 +17,7 @@ served with no startup time. Unlike mod_python (or `mod_perl`_), a FastCGI
|
||||
process doesn't run inside the Web server process, but in a separate,
|
||||
persistent process.
|
||||
|
||||
.. _current preferred setup: http://www.djangoproject.com/documentation/modpython/
|
||||
.. _current preferred setup: ../modpython/
|
||||
.. _Apache: http://httpd.apache.org/
|
||||
.. _mod_python: http://www.modpython.org/
|
||||
.. _mod_perl: http://perl.apache.org/
|
||||
|
@ -29,8 +29,8 @@ To install the flatpages app, follow these steps:
|
||||
to your MIDDLEWARE_CLASSES_ setting.
|
||||
3. Run the command ``manage.py syncdb``.
|
||||
|
||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
||||
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes
|
||||
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||
.. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
|
||||
|
||||
How it works
|
||||
============
|
||||
@ -63,9 +63,9 @@ resort.
|
||||
|
||||
For more on middleware, read the `middleware docs`_.
|
||||
|
||||
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id
|
||||
.. _RequestContext: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
|
||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _SITE_ID: ../settings/#site-id
|
||||
.. _RequestContext: ../templates_python/#subclassing-context-djangocontext
|
||||
.. _middleware docs: ../middleware/
|
||||
|
||||
How to add, change and delete flatpages
|
||||
=======================================
|
||||
@ -84,9 +84,9 @@ Flatpages are represented by a standard `Django model`_, which lives in
|
||||
`django/contrib/flatpages/models.py`_. You can access flatpage objects via the
|
||||
`Django database API`_.
|
||||
|
||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _Django model: ../model_api/
|
||||
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
|
||||
.. _Django database API: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _Django database API: ../db_api/
|
||||
|
||||
Flatpage templates
|
||||
==================
|
||||
|
@ -2,15 +2,27 @@
|
||||
Forms, fields, and manipulators
|
||||
===============================
|
||||
|
||||
Forwards-compatibility note
|
||||
===========================
|
||||
|
||||
The legacy forms/manipulators system described in this document is going to be
|
||||
replaced in the next Django release. If you're starting from scratch, we
|
||||
strongly encourage you not to waste your time learning this. Instead, learn and
|
||||
use the django.newforms system, which we have begun to document in the
|
||||
`newforms documentation`_.
|
||||
|
||||
If you have legacy form/manipulator code, read the "Migration plan" section in
|
||||
that document to understand how we're making the switch.
|
||||
|
||||
.. _newforms documentation: ../newforms/
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Once you've got a chance to play with Django's admin interface, you'll probably
|
||||
wonder if the fantastic form validation framework it uses is available to user
|
||||
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,
|
||||
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::
|
||||
@ -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``,
|
||||
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
|
||||
**Manipulator** framework. A manipulator is a utility class tied to a given
|
||||
model that "knows" how to create or modify instances of that model and how to
|
||||
validate data for the object. Manipulators come in two flavors:
|
||||
``AddManipulators`` and ``ChangeManipulators``. Functionally they are quite
|
||||
similar, but the former knows how to create new instances of the model, while
|
||||
the latter modifies existing instances. Both types of classes are automatically
|
||||
created when you define a new class::
|
||||
**automatic Manipulator** framework. An automatic manipulator is a utility
|
||||
class tied to a given model that "knows" how to create or modify instances of
|
||||
that model and how to validate data for the object. Automatic Manipulators come
|
||||
in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally
|
||||
they are quite similar, but the former knows how to create new instances of the
|
||||
model, while the latter modifies existing instances. Both types of classes are
|
||||
automatically created when you define a new class::
|
||||
|
||||
>>> from mysite.myapp.models import Place
|
||||
>>> Place.AddManipulator
|
||||
@ -653,6 +665,6 @@ fails. If no message is passed in, a default message is used.
|
||||
the executable specified in the ``JING_PATH`` setting (see the settings_
|
||||
document for more details).
|
||||
|
||||
.. _`generic views`: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _`models API`: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _settings: http://www.djangoproject.com/documentation/settings/
|
||||
.. _`generic views`: ../generic_views/
|
||||
.. _`models API`: ../model_api/
|
||||
.. _settings: ../settings/
|
||||
|
@ -71,7 +71,7 @@ are first evaluated, so if you want to pass in a QuerySet via
|
||||
``extra_context`` that is always fresh you need to wrap it in a function or
|
||||
lambda that returns the QuerySet.
|
||||
|
||||
.. _database API docs: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _database API docs: ../db_api/
|
||||
|
||||
"Simple" generic views
|
||||
======================
|
||||
@ -99,7 +99,7 @@ which is a dictionary of the parameters captured in the URL.
|
||||
dictionary is callable, the generic view will call it
|
||||
just before rendering the template. (**This is new in the
|
||||
Django development version.**)
|
||||
|
||||
|
||||
**Example:**
|
||||
|
||||
Given the following URL patterns::
|
||||
@ -205,11 +205,11 @@ If ``template_name`` isn't specified, this view will use the template
|
||||
``<app_label>/<model_name>_archive.html`` by default, where:
|
||||
|
||||
* ``<model_name>`` is your model's name in all lowercase. For a model
|
||||
``StaffMember``, that'd be ``staffmember``.
|
||||
``StaffMember``, that'd be ``staffmember``.
|
||||
|
||||
* ``<app_label>`` is the right-most part of the full Python path to
|
||||
your model's app. For example, if your model lives in
|
||||
``apps/blog/models.py``, that'd be ``blog``.
|
||||
your model's app. For example, if your model lives in
|
||||
``apps/blog/models.py``, that'd be ``blog``.
|
||||
|
||||
**Template context:**
|
||||
|
||||
@ -223,7 +223,7 @@ In addition to ``extra_context``, the template's context will be:
|
||||
by ``date_field``. For example, if ``num_latest`` is ``10``, then
|
||||
``latest`` will be a list of the latest 10 objects in ``queryset``.
|
||||
|
||||
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
|
||||
.. _RequestContext docs: ../templates_python/#subclassing-context-djangocontext
|
||||
|
||||
``django.views.generic.date_based.archive_year``
|
||||
------------------------------------------------
|
||||
@ -266,9 +266,9 @@ to ``True``.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``make_object_list``: A boolean specifying whether to retrieve the full
|
||||
list of objects for this year and pass those to the template. If ``True``,
|
||||
@ -360,9 +360,9 @@ date in the *future* are not displayed unless you set ``allow_future`` to
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -441,9 +441,9 @@ in the *future* are not displayed unless you set ``allow_future`` to ``True``.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -526,9 +526,9 @@ you set ``allow_future`` to ``True``.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -638,7 +638,7 @@ future, the view will throw a 404 error by default, unless you set
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -710,9 +710,9 @@ A page representing a list of objects.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -824,7 +824,7 @@ A page representing an individual object.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -902,7 +902,7 @@ If ``template_name`` isn't specified, this view will use the template
|
||||
|
||||
In addition to ``extra_context``, the template's context will be:
|
||||
|
||||
* ``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
|
||||
template system.
|
||||
|
||||
@ -916,8 +916,8 @@ In addition to ``extra_context``, the template's context will be:
|
||||
See the `manipulator and formfield documentation`_ for more information
|
||||
about using ``FormWrapper`` objects in templates.
|
||||
|
||||
.. _authentication system: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _manipulator and formfield documentation: http://www.djangoproject.com/documentation/forms/
|
||||
.. _authentication system: ../authentication/
|
||||
.. _manipulator and formfield documentation: ../forms/
|
||||
|
||||
``django.views.generic.create_update.update_object``
|
||||
----------------------------------------------------
|
||||
@ -973,7 +973,7 @@ object. This uses the automatic manipulators that come with Django models.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
|
||||
**Template name:**
|
||||
|
||||
@ -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:
|
||||
|
||||
* ``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
|
||||
template system.
|
||||
|
||||
@ -1054,7 +1054,7 @@ contain a form that POSTs to the same URL.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
|
||||
**Template name:**
|
||||
|
||||
|
@ -53,7 +53,7 @@ internationalization machinery. See the `documentation for USE_I18N`_.
|
||||
You'll probably also want to remove ``'django.core.context_processors.i18n'``
|
||||
from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
|
||||
|
||||
.. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n
|
||||
.. _documentation for USE_I18N: ../settings/#use-i18n
|
||||
|
||||
How to specify translation strings
|
||||
==================================
|
||||
@ -175,7 +175,7 @@ class, though::
|
||||
verbose_name = _('my thing')
|
||||
verbose_name_plural = _('mythings')
|
||||
|
||||
.. _Django models: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _Django models: ../model_api/
|
||||
|
||||
Pluralization
|
||||
~~~~~~~~~~~~~
|
||||
@ -274,7 +274,7 @@ translation string. Example::
|
||||
In this case, both the tag and the filter will see the already-translated
|
||||
string, so they don't need to be aware of translations.
|
||||
|
||||
.. _Django templates: http://www.djangoproject.com/documentation/templates_python/
|
||||
.. _Django templates: ../templates_python/
|
||||
|
||||
How to create language files
|
||||
============================
|
||||
@ -394,7 +394,7 @@ That's it. Your translations are ready for use.
|
||||
please let us know! See `Submitting and maintaining translations`_ for
|
||||
the steps to take.
|
||||
|
||||
.. _Submitting and maintaining translations: http://www.djangoproject.com/documentation/contributing/
|
||||
.. _Submitting and maintaining translations: ../contributing/
|
||||
|
||||
How Django discovers language preference
|
||||
========================================
|
||||
@ -472,7 +472,7 @@ Notes:
|
||||
selection to German and English (and any sublanguage, like de-ch or
|
||||
en-us).
|
||||
|
||||
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
|
||||
.. _LANGUAGES setting: ../settings/#languages
|
||||
|
||||
* If you define a custom ``LANGUAGES`` setting, as explained in the
|
||||
previous bullet, it's OK to mark the languages as translation strings
|
||||
@ -530,10 +530,10 @@ Note that, with static (middleware-less) translation, the language is in
|
||||
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
|
||||
in ``request.LANGUAGE_CODE``.
|
||||
|
||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
||||
.. _middleware documentation: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _session: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
||||
.. _settings file: ../settings/
|
||||
.. _middleware documentation: ../middleware/
|
||||
.. _session: ../sessions/
|
||||
.. _request object: ../request_response/#httprequest-objects
|
||||
|
||||
The ``set_language`` redirect view
|
||||
==================================
|
||||
@ -599,7 +599,7 @@ message file. The choice is yours.
|
||||
of the settings file to determine this, and a settings file doesn't exist
|
||||
if you're manually configuring your settings.)
|
||||
|
||||
.. _settings documentation: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||
.. _settings documentation: ../settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||
|
||||
All message file repositories are structured the same way. They are:
|
||||
|
||||
|
@ -38,7 +38,7 @@ each platform.
|
||||
.. _Apache: http://httpd.apache.org/
|
||||
.. _mod_python: http://www.modpython.org/
|
||||
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
||||
.. _How to use Django with mod_python: http://www.djangoproject.com/documentation/modpython/
|
||||
.. _How to use Django with mod_python: ../modpython/
|
||||
.. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
||||
|
||||
Get your database running
|
||||
@ -113,14 +113,14 @@ latest bug fixes and improvements, follow these instructions:
|
||||
|
||||
svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django
|
||||
|
||||
4. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
|
||||
3. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
|
||||
system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
|
||||
(Windows). This step simply lets you type ``django-admin.py`` from within
|
||||
any directory, rather than having to qualify the command with the full path
|
||||
to the file.
|
||||
|
||||
You *don't* have to run ``python setup.py install``, because that command
|
||||
takes care of steps 3 and 4 for you.
|
||||
takes care of steps 2 and 3 for you.
|
||||
|
||||
When you want to update your copy of the Django source code, just run the
|
||||
command ``svn update`` from within the ``django`` directory. When you do this,
|
||||
|
@ -9,7 +9,7 @@ utilities to automate as much of this process as possible.
|
||||
This document assumes you know the Django basics, as covered in the
|
||||
`official tutorial`_.
|
||||
|
||||
.. _official tutorial: http://www.djangoproject.com/documentation/tutorial1/
|
||||
.. _official tutorial: ../tutorial1/
|
||||
|
||||
Give Django your database parameters
|
||||
====================================
|
||||
@ -22,18 +22,16 @@ what the name of the database is. Do that by editing these settings in your
|
||||
* `DATABASE_ENGINE`_
|
||||
* `DATABASE_USER`_
|
||||
* `DATABASE_PASSWORD`_
|
||||
* `DATABASE_NAME`_
|
||||
* `DATABASE_HOST`_
|
||||
* `DATABASE_PORT`_
|
||||
|
||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
||||
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name
|
||||
.. _DATABASE_ENGINE: http://www.djangoproject.com/documentation/settings/#database-engine
|
||||
.. _DATABASE_USER: http://www.djangoproject.com/documentation/settings/#database-user
|
||||
.. _DATABASE_PASSWORD: http://www.djangoproject.com/documentation/settings/#database-password
|
||||
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name
|
||||
.. _DATABASE_HOST: http://www.djangoproject.com/documentation/settings/#database-host
|
||||
.. _DATABASE_PORT: http://www.djangoproject.com/documentation/settings/#database-port
|
||||
.. _settings file: ../settings/
|
||||
.. _DATABASE_NAME: ../settings/#database-name
|
||||
.. _DATABASE_ENGINE: ../settings/#database-engine
|
||||
.. _DATABASE_USER: ../settings/#database-user
|
||||
.. _DATABASE_PASSWORD: ../settings/#database-password
|
||||
.. _DATABASE_HOST: ../settings/#database-host
|
||||
.. _DATABASE_PORT: ../settings/#database-port
|
||||
|
||||
Auto-generate the models
|
||||
========================
|
||||
@ -54,7 +52,7 @@ Once you've cleaned up your models, name the file ``models.py`` and put it in
|
||||
the Python package that holds your app. Then add the app to your
|
||||
``INSTALLED_APPS`` setting.
|
||||
|
||||
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _django-admin.py documentation: ../django_admin/
|
||||
|
||||
Install the core Django tables
|
||||
==============================
|
||||
|
@ -47,7 +47,7 @@ Enables site-wide cache. If this is enabled, each Django-powered page will be
|
||||
cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See
|
||||
the `cache documentation`_.
|
||||
|
||||
.. _`cache documentation`: http://www.djangoproject.com/documentation/cache/#the-per-site-cache
|
||||
.. _`cache documentation`: ../cache/#the-per-site-cache
|
||||
|
||||
django.middleware.common.CommonMiddleware
|
||||
-----------------------------------------
|
||||
@ -106,9 +106,10 @@ django.middleware.http.SetRemoteAddrFromForwardedFor
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Sets ``request['REMOTE_ADDR']`` based on ``request.['HTTP_X_FORWARDED_FOR']``,
|
||||
if the latter is set. This is useful if you're sitting behind a reverse proxy
|
||||
that causes each request's ``REMOTE_ADDR`` to be set to ``127.0.0.1``.
|
||||
Sets ``request.META['REMOTE_ADDR']`` based on
|
||||
``request.META['HTTP_X_FORWARDED_FOR']``, if the latter is set. This is useful
|
||||
if you're sitting behind a reverse proxy that causes each request's
|
||||
``REMOTE_ADDR`` to be set to ``127.0.0.1``.
|
||||
|
||||
**Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're
|
||||
not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do
|
||||
@ -122,7 +123,7 @@ django.contrib.sessions.middleware.SessionMiddleware
|
||||
|
||||
Enables session support. See the `session documentation`_.
|
||||
|
||||
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _`session documentation`: ../sessions/
|
||||
|
||||
django.contrib.auth.middleware.AuthenticationMiddleware
|
||||
-------------------------------------------------------
|
||||
@ -130,7 +131,7 @@ django.contrib.auth.middleware.AuthenticationMiddleware
|
||||
Adds the ``user`` attribute, representing the currently-logged-in user, to
|
||||
every incoming ``HttpRequest`` object. See `Authentication in Web requests`_.
|
||||
|
||||
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests
|
||||
.. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
|
||||
|
||||
django.middleware.transaction.TransactionMiddleware
|
||||
---------------------------------------------------
|
||||
@ -146,7 +147,7 @@ the same transaction control as the view functions.
|
||||
|
||||
See the `transaction management documentation`_.
|
||||
|
||||
.. _`transaction management documentation`: http://www.djangoproject.com/documentation/transactions/
|
||||
.. _`transaction management documentation`: ../transactions/
|
||||
|
||||
Writing your own middleware
|
||||
===========================
|
||||
|
@ -94,7 +94,7 @@ Django places only two restrictions on model field names:
|
||||
the way Django's query lookup syntax works. For example::
|
||||
|
||||
class Example(models.Model):
|
||||
foo__bar = models.IntegerField() 'foo__bar' has two underscores!
|
||||
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
|
||||
|
||||
These limitations can be worked around, though, because your field name doesn't
|
||||
necessarily have to match your database column name. See `db_column`_ below.
|
||||
@ -874,6 +874,10 @@ the relationship should work. All are optional:
|
||||
force Django to add the descriptor for the reverse
|
||||
relationship, allowing ``ManyToMany`` relationships to be
|
||||
non-symmetrical.
|
||||
|
||||
``db_table`` The name of the table to create for storing the many-to-many
|
||||
data. If this is not provided, Django will assume a default
|
||||
name based upon the names of the two tables being joined.
|
||||
|
||||
======================= ============================================================
|
||||
|
||||
@ -1268,6 +1272,24 @@ A few special cases to note about ``list_display``:
|
||||
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
|
||||
colored_name.allow_tags = True
|
||||
|
||||
* If the string given is a method of the model that returns True or False
|
||||
Django will display a pretty "on" or "off" icon if you give the method a
|
||||
``boolean`` attribute whose value is ``True``.
|
||||
|
||||
Here's a full example model::
|
||||
|
||||
class Person(models.Model):
|
||||
first_name = models.CharField(maxlength=50)
|
||||
birthday = models.DateField()
|
||||
|
||||
class Admin:
|
||||
list_display = ('name', 'born_in_fifties')
|
||||
|
||||
def born_in_fifties(self):
|
||||
return self.birthday.strftime('%Y')[:3] == 5
|
||||
born_in_fifties.boolean = True
|
||||
|
||||
|
||||
* The ``__str__()`` method is just as valid in ``list_display`` as any
|
||||
other model method, so it's perfectly OK to do this::
|
||||
|
||||
@ -1390,7 +1412,10 @@ This should be set to a list of field names that will be searched whenever
|
||||
somebody submits a search query in that text box.
|
||||
|
||||
These fields should be some kind of text field, such as ``CharField`` or
|
||||
``TextField``.
|
||||
``TextField``. You can also perform a related lookup on a ``ForeignKey`` with
|
||||
the lookup API "follow" notation::
|
||||
|
||||
search_fields = ['foreign_key__related_fieldname']
|
||||
|
||||
When somebody does a search in the admin search box, Django splits the search
|
||||
query into words and returns all objects that contain each of the words, case
|
||||
|
@ -20,7 +20,7 @@ You may also be interested in `How to use Django with FastCGI`_.
|
||||
.. _mod_perl: http://perl.apache.org/
|
||||
.. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html
|
||||
.. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html
|
||||
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/
|
||||
.. _How to use Django with FastCGI: ../fastcgi/
|
||||
|
||||
Basic configuration
|
||||
===================
|
||||
|
@ -2,9 +2,9 @@
|
||||
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.
|
||||
``django.newforms`` is Django's fantastic new form-handling library. It's a
|
||||
replacement for ``django.forms``, the old form/manipulator/validation
|
||||
framework. This document explains how to use this new library.
|
||||
|
||||
Migration plan
|
||||
==============
|
||||
@ -13,18 +13,23 @@ Migration plan
|
||||
-- i.e., it's not available in the Django 0.95 release. For the next Django
|
||||
release, our plan is to do the following:
|
||||
|
||||
* Move the current ``django.forms`` to ``django.oldforms``. This will allow
|
||||
for an eased migration of form code. You'll just have to change your
|
||||
import statements::
|
||||
* 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
|
||||
|
||||
* Move the current ``django.newforms`` to ``django.forms``.
|
||||
* At an undecided future date, we will move the current ``django.newforms``
|
||||
to ``django.forms``. This will be a backwards-incompatible change, and
|
||||
anybody who is still using the old version of ``django.forms`` at that
|
||||
time will need to change their import statements, as described in the
|
||||
previous bullet.
|
||||
|
||||
* We will remove ``django.oldforms`` in the release *after* the next Django
|
||||
release -- the release that comes after the release in which we're
|
||||
creating ``django.oldforms``.
|
||||
creating the new ``django.forms``.
|
||||
|
||||
With this in mind, we recommend you use the following import statement when
|
||||
using ``django.newforms``::
|
||||
@ -46,9 +51,14 @@ too messy. The choice is yours.
|
||||
Overview
|
||||
========
|
||||
|
||||
As the ``django.forms`` 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.
|
||||
As with 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:
|
||||
|
||||
@ -62,13 +72,751 @@ The library deals with these concepts:
|
||||
* **Form** -- A collection of fields that knows how to validate itself and
|
||||
display itself as HTML.
|
||||
|
||||
The library is decoupled from the other Django components, such as the database
|
||||
layer, views and templates. It relies only on Django settings, a couple of
|
||||
``django.utils`` helper functions and Django's internationalization hooks (but
|
||||
you're not required to be using internationalization features to use this
|
||||
library).
|
||||
|
||||
Form objects
|
||||
============
|
||||
|
||||
Using forms with templates
|
||||
==========================
|
||||
The primary way of using the ``newforms`` library is to create a form object.
|
||||
Do this by subclassing ``django.newforms.Form`` and specifying the form's
|
||||
fields, in a declarative style that you'll be familiar with if you've used
|
||||
Django database models. In this section, we'll iteratively develop a form
|
||||
object that you might use to implement "contact me" functionality on your
|
||||
personal Web site.
|
||||
|
||||
Using forms in views
|
||||
====================
|
||||
Start with this basic ``Form`` subclass, which we'll call ``ContactForm``::
|
||||
|
||||
from django import newforms as forms
|
||||
|
||||
class ContactForm(forms.Form):
|
||||
subject = forms.CharField(max_length=100)
|
||||
message = forms.CharField()
|
||||
sender = forms.EmailField()
|
||||
cc_myself = forms.BooleanField()
|
||||
|
||||
A form is composed of ``Field`` objects. In this case, our form has four
|
||||
fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain
|
||||
the different types of fields -- e.g., ``CharField`` and ``EmailField`` --
|
||||
shortly.
|
||||
|
||||
Creating ``Form`` instances
|
||||
---------------------------
|
||||
|
||||
A ``Form`` instance is either **bound** or **unbound** to a set of data.
|
||||
|
||||
* If it's **bound** to a set of data, it's capable of validating that data
|
||||
and rendering the form as HTML with the data displayed in the HTML.
|
||||
|
||||
* If it's **unbound**, it cannot do validation (because there's no data to
|
||||
validate!), but it can still render the blank form as HTML.
|
||||
|
||||
To create an unbound ``Form`` instance, simply instantiate the class::
|
||||
|
||||
>>> f = ContactForm()
|
||||
|
||||
To bind data to a form, pass the data as a dictionary as the first parameter to
|
||||
your ``Form`` class constructor::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
|
||||
In this dictionary, the keys are the field names, which correspond to the
|
||||
attributes in your ``Form`` class. The values are the data you're trying
|
||||
to validate. These will usually be strings, but there's no requirement that
|
||||
they be strings; the type of data you pass depends on the ``Field``, as we'll
|
||||
see in a moment.
|
||||
|
||||
If you need to distinguish between bound and unbound form instances at runtime,
|
||||
check the value of the form's ``is_bound`` attribute::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.is_bound
|
||||
False
|
||||
>>> f = ContactForm({'subject': 'hello'})
|
||||
>>> f.is_bound
|
||||
True
|
||||
|
||||
Note that passing an empty dictionary creates a *bound* form with empty data::
|
||||
|
||||
>>> f = ContactForm({})
|
||||
>>> f.is_bound
|
||||
True
|
||||
|
||||
If you have a bound ``Form`` instance and want to change the data somehow, or
|
||||
if you want to bind an unbound ``Form`` instance to some data, create another
|
||||
``Form`` instance. There is no way to change data in a ``Form`` instance. Once
|
||||
a ``Form`` instance has been created, you should consider its data immutable,
|
||||
whether it has data or not.
|
||||
|
||||
Using forms to validate data
|
||||
----------------------------
|
||||
|
||||
The primary task of a ``Form`` object is to validate data. With a bound
|
||||
``Form`` instance, call the ``is_valid()`` method to run validation and return
|
||||
a boolean designating whether the data was valid::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
True
|
||||
|
||||
Let's try with some invalid data. In this case, ``subject`` is blank (an error,
|
||||
because all fields are required by default) and ``sender`` is not a valid
|
||||
e-mail address::
|
||||
|
||||
>>> data = {'subject': '',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'invalid e-mail address',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
False
|
||||
|
||||
Access the ``Form`` attribute ``errors`` to get a dictionary of error messages::
|
||||
|
||||
>>> f.errors
|
||||
{'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']}
|
||||
|
||||
In this dictionary, the keys are the field names, and the values are lists of
|
||||
Unicode strings representing the error messages. The error messages are stored
|
||||
in lists because a field can have multiple error messages.
|
||||
|
||||
You can access ``errors`` without having to call ``is_valid()`` first. The
|
||||
form's data will be validated the first time either you call ``is_valid()`` or
|
||||
access ``errors``.
|
||||
|
||||
Behavior of unbound forms
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It's meaningless to validate a form with no data, but, for the record, here's
|
||||
what happens with unbound forms::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.is_valid()
|
||||
False
|
||||
>>> f.errors
|
||||
{}
|
||||
|
||||
Accessing "clean" data
|
||||
----------------------
|
||||
|
||||
Each ``Field`` in a ``Form`` class is responsible not only for validating data,
|
||||
but also for "cleaning" it -- normalizing it to a consistent format. This is a
|
||||
nice feature, because it allows data for a particular field to be input in
|
||||
a variety of ways, always resulting in consistent output.
|
||||
|
||||
For example, ``DateField`` normalizes input into a Python ``datetime.date``
|
||||
object. Regardless of whether you pass it a string in the format
|
||||
``'1994-07-15'``, a ``datetime.date`` object or a number of other formats,
|
||||
``DateField`` will always normalize it to a ``datetime.date`` object as long as
|
||||
it's valid.
|
||||
|
||||
Once you've created a ``Form`` instance with a set of data and validated it,
|
||||
you can access the clean data via the ``clean_data`` attribute of the ``Form``
|
||||
object::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.clean_data
|
||||
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||
|
||||
Note that any text-based field -- such as ``CharField`` or ``EmailField`` --
|
||||
always cleans the input into a Unicode string. We'll cover the encoding
|
||||
implications later in this document.
|
||||
|
||||
If your data does *not* validate, your ``Form`` instance will not have a
|
||||
``clean_data`` attribute::
|
||||
|
||||
>>> data = {'subject': '',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'invalid e-mail address',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
False
|
||||
>>> f.clean_data
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
||||
|
||||
``clean_data`` will always *only* contain a key for fields defined in the
|
||||
``Form``, even if you pass extra data when you define the ``Form``. In this
|
||||
example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
|
||||
but ``clean_data`` contains only the form's fields::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True,
|
||||
... 'extra_field_1': 'foo',
|
||||
... 'extra_field_2': 'bar',
|
||||
... 'extra_field_3': 'baz'}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.clean_data # Doesn't contain extra_field_1, etc.
|
||||
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||
|
||||
Behavior of unbound forms
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It's meaningless to request "clean" data in a form with no data, but, for the
|
||||
record, here's what happens with unbound forms::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.clean_data
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
||||
|
||||
Outputting forms as HTML
|
||||
------------------------
|
||||
|
||||
The second task of a ``Form`` object is to render itself as HTML. To do so,
|
||||
simply ``print`` it::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> print f
|
||||
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
|
||||
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
|
||||
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
|
||||
|
||||
If the form is bound to data, the HTML output will include that data
|
||||
appropriately. For example, if a field is represented by an
|
||||
``<input type="text">``, the data will be in the ``value`` attribute. If a
|
||||
field is represented by an ``<input type="checkbox">``, then that HTML will
|
||||
include ``checked="checked"`` if appropriate::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> print f
|
||||
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr>
|
||||
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr>
|
||||
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></td></tr>
|
||||
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
|
||||
|
||||
This default output is a two-column HTML table, with a ``<tr>`` for each field.
|
||||
Notice the following:
|
||||
|
||||
* For flexibility, the output does *not* include the ``<table>`` and
|
||||
``</table>`` tags, nor does it include the ``<form>`` and ``</form>``
|
||||
tags or an ``<input type="submit">`` tag. It's your job to do that.
|
||||
|
||||
* Each field type has a default HTML representation. ``CharField`` and
|
||||
``EmailField`` are represented by an ``<input type="text">``.
|
||||
``BooleanField`` is represented by an ``<input type="checkbox">``. Note
|
||||
these are merely sensible defaults; you can specify which HTML to use for
|
||||
a given field by using widgets, which we'll explain shortly.
|
||||
|
||||
* The HTML ``name`` for each tag is taken directly from its attribute name
|
||||
in the ``ContactForm`` class.
|
||||
|
||||
* The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and
|
||||
``'Cc myself:'`` is generated from the field name by converting all
|
||||
underscores to spaces and upper-casing the first letter. Again, note
|
||||
these are merely sensible defaults; you can also specify labels manually.
|
||||
|
||||
* Each text label is surrounded in an HTML ``<label>`` tag, which points
|
||||
to the appropriate form field via its ``id``. Its ``id``, in turn, is
|
||||
generated by prepending ``'id_'`` to the field name. The ``id``
|
||||
attributes and ``<label>`` tags are included in the output by default, to
|
||||
follow best practices, but you can change that behavior.
|
||||
|
||||
Although ``<table>`` output is the default output style when you ``print`` a
|
||||
form, other output styles are available. Each style is available as a method on
|
||||
a form object, and each rendering method returns a Unicode object.
|
||||
|
||||
``as_p()``
|
||||
~~~~~~~~~~
|
||||
|
||||
``Form.as_p()`` renders the form as a series of ``<p>`` tags, with each ``<p>``
|
||||
containing one field::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.as_p()
|
||||
u'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>'
|
||||
>>> print f.as_p()
|
||||
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>
|
||||
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>
|
||||
<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>
|
||||
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
|
||||
|
||||
``as_ul()``
|
||||
~~~~~~~~~~~
|
||||
|
||||
``Form.as_ul()`` renders the form as a series of ``<li>`` tags, with each
|
||||
``<li>`` containing one field. It does *not* include the ``<ul>`` or ``</ul>``,
|
||||
so that you can specify any HTML attributes on the ``<ul>`` for flexibility::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.as_ul()
|
||||
u'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>'
|
||||
>>> print f.as_ul()
|
||||
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>
|
||||
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>
|
||||
<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>
|
||||
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>
|
||||
|
||||
``as_table()``
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Finally, ``Form.as_table()`` outputs the form as an HTML ``<table>``. This is
|
||||
exactly the same as ``print``. In fact, when you ``print`` a form object, it
|
||||
calls its ``as_table()`` method behind the scenes::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.as_table()
|
||||
u'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>'
|
||||
>>> print f.as_table()
|
||||
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
|
||||
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
|
||||
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
|
||||
|
||||
Configuring HTML ``<label>`` tags
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An HTML ``<label>`` tag designates which label text is associated with which
|
||||
form element. This small enhancement makes forms more usable and more accessible
|
||||
to assistive devices. It's always a good idea to use ``<label>`` tags.
|
||||
|
||||
By default, the form rendering methods include HTML ``id`` attributes on the
|
||||
form elements and corresponding ``<label>`` tags around the labels. The ``id``
|
||||
attribute values are generated by prepending ``id_`` to the form field names.
|
||||
This behavior is configurable, though, if you want to change the ``id``
|
||||
convention or remove HTML ``id`` attributes and ``<label>`` tags entirely.
|
||||
|
||||
Use the ``auto_id`` argument to the ``Form`` constructor to control the label
|
||||
and ``id`` behavior. This argument must be ``True``, ``False`` or a string.
|
||||
|
||||
If ``auto_id`` is ``False``, then the form output will not include ``<label>``
|
||||
tags nor ``id`` attributes::
|
||||
|
||||
>>> f = ContactForm(auto_id=False)
|
||||
>>> print f.as_table()
|
||||
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
|
||||
<tr><th>Sender:</th><td><input type="text" name="sender" /></td></tr>
|
||||
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li>Subject: <input type="text" name="subject" maxlength="100" /></li>
|
||||
<li>Message: <input type="text" name="message" /></li>
|
||||
<li>Sender: <input type="text" name="sender" /></li>
|
||||
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
|
||||
>>> print f.as_p()
|
||||
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
|
||||
<p>Message: <input type="text" name="message" /></p>
|
||||
<p>Sender: <input type="text" name="sender" /></p>
|
||||
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
|
||||
|
||||
If ``auto_id`` is set to ``True``, then the form output *will* include
|
||||
``<label>`` tags and will simply use the field name as its ``id`` for each form
|
||||
field::
|
||||
|
||||
>>> f = ContactForm(auto_id=True)
|
||||
>>> print f.as_table()
|
||||
<tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr>
|
||||
<tr><th><label for="sender">Sender:</label></th><td><input type="text" name="sender" id="sender" /></td></tr>
|
||||
<tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></li>
|
||||
<li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li>
|
||||
<li><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></li>
|
||||
<li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li>
|
||||
>>> print f.as_p()
|
||||
<p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></p>
|
||||
<p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p>
|
||||
<p><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></p>
|
||||
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p>
|
||||
|
||||
If ``auto_id`` is set to a string containing the format character ``'%s'``,
|
||||
then the form output will include ``<label>`` tags, and will generate ``id``
|
||||
attributes based on the format string. For example, for a format string
|
||||
``'field_%s'``, a field named ``subject`` will get the ``id``
|
||||
``'field_subject'``. Continuing our example::
|
||||
|
||||
>>> f = ContactForm(auto_id='id_for_%s')
|
||||
>>> print f.as_table()
|
||||
<tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr>
|
||||
<tr><th><label for="id_for_sender">Sender:</label></th><td><input type="text" name="sender" id="id_for_sender" /></td></tr>
|
||||
<tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
|
||||
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li>
|
||||
<li><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></li>
|
||||
<li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
|
||||
>>> print f.as_p()
|
||||
<p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></p>
|
||||
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p>
|
||||
<p><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></p>
|
||||
<p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p>
|
||||
|
||||
If ``auto_id`` is set to any other true value -- such as a string that doesn't
|
||||
include ``%s`` -- then the library will act as if ``auto_id`` is ``True``.
|
||||
|
||||
By default, ``auto_id`` is set to the string ``'id_%s'``.
|
||||
|
||||
Notes on field ordering
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the ``as_p()``, ``as_ul()`` and ``as_table()`` shortcuts, the fields are
|
||||
displayed in the order in which you define them in your form class. For
|
||||
example, in the ``ContactForm`` example, the fields are defined in the order
|
||||
``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML
|
||||
output, just change the order in which those fields are listed in the class.
|
||||
|
||||
How errors are displayed
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you render a bound ``Form`` object, the act of rendering will automatically
|
||||
run the form's validation if it hasn't already happened, and the HTML output
|
||||
will include the validation errors as a ``<ul>`` near the field. The particular
|
||||
positioning of the error messages depends on the output method you're using::
|
||||
|
||||
>>> data = {'subject': '',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'invalid e-mail address',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data, auto_id=False)
|
||||
>>> print f.as_table()
|
||||
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr>
|
||||
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul><input type="text" name="sender" value="invalid e-mail address" /></td></tr>
|
||||
<tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li>
|
||||
<li>Message: <input type="text" name="message" value="Hi there" /></li>
|
||||
<li><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul>Sender: <input type="text" name="sender" value="invalid e-mail address" /></li>
|
||||
<li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li>
|
||||
>>> print f.as_p()
|
||||
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
|
||||
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
|
||||
<p>Message: <input type="text" name="message" value="Hi there" /></p>
|
||||
<p><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul></p>
|
||||
<p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p>
|
||||
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
|
||||
|
||||
More granular output
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``as_p()``, ``as_ul()`` and ``as_table()`` methods are simply shortcuts for
|
||||
lazy developers -- they're not the only way a form object can be displayed.
|
||||
|
||||
To display the HTML for a single field in your form, use dictionary lookup
|
||||
syntax using the field's name as the key, and print the resulting object::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> print f['subject']
|
||||
<input id="id_subject" type="text" name="subject" maxlength="100" />
|
||||
>>> print f['message']
|
||||
<input type="text" name="message" id="id_message" />
|
||||
>>> print f['sender']
|
||||
<input type="text" name="sender" id="id_sender" />
|
||||
>>> print f['cc_myself']
|
||||
<input type="checkbox" name="cc_myself" id="id_cc_myself" />
|
||||
|
||||
Call ``str()`` or ``unicode()`` on the field to get its rendered HTML as a
|
||||
string or Unicode object, respectively::
|
||||
|
||||
>>> str(f['subject'])
|
||||
'<input id="id_subject" type="text" name="subject" maxlength="100" />'
|
||||
>>> unicode(f['subject'])
|
||||
u'<input id="id_subject" type="text" name="subject" maxlength="100" />'
|
||||
|
||||
The field-specific output honors the form object's ``auto_id`` setting::
|
||||
|
||||
>>> f = ContactForm(auto_id=False)
|
||||
>>> print f['message']
|
||||
<input type="text" name="message" />
|
||||
>>> f = ContactForm(auto_id='id_%s')
|
||||
>>> print f['message']
|
||||
<input type="text" name="message" id="id_message" />
|
||||
|
||||
For a field's list of errors, access the field's ``errors`` attribute. This
|
||||
is a list-like object that is displayed as an HTML ``<ul>`` when printed::
|
||||
|
||||
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
|
||||
>>> f = ContactForm(data, auto_id=False)
|
||||
>>> print f['message']
|
||||
<input type="text" name="message" />
|
||||
>>> f['message'].errors
|
||||
[u'This field is required.']
|
||||
>>> print f['message'].errors
|
||||
<ul class="errorlist"><li>This field is required.</li></ul>
|
||||
>>> f['subject'].errors
|
||||
[]
|
||||
>>> print f['subject'].errors
|
||||
|
||||
>>> str(f['subject'].errors)
|
||||
''
|
||||
|
||||
Fields
|
||||
======
|
||||
|
||||
When you create a ``Form`` class, the most important part is defining the
|
||||
fields of the form. Each field has custom validation logic, along with a few
|
||||
other hooks.
|
||||
|
||||
Although the primary way you'll use ``Field`` classes is in ``Form`` classes,
|
||||
you can also instantiate them and use them directly to get a better idea of
|
||||
how they work. Each ``Field`` instance has a ``clean()`` method, which takes
|
||||
a single argument and either raises a ``django.newforms.ValidationError``
|
||||
exception or returns the clean value::
|
||||
|
||||
>>> f = forms.EmailField()
|
||||
>>> f.clean('foo@example.com')
|
||||
u'foo@example.com'
|
||||
>>> f.clean(u'foo@example.com')
|
||||
u'foo@example.com'
|
||||
>>> f.clean('invalid e-mail address')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid e-mail address.']
|
||||
|
||||
If you've used Django's old forms/validation framework, take care in noticing
|
||||
this ``ValidationError`` is different than the previous ``ValidationError``.
|
||||
This one lives at ``django.newforms.ValidationError`` rather than
|
||||
``django.core.validators.ValidationError``.
|
||||
|
||||
Core field arguments
|
||||
--------------------
|
||||
|
||||
Each ``Field`` class constructor takes at least these arguments. Some
|
||||
``Field`` classes take additional, field-specific arguments, but the following
|
||||
should *always* be available:
|
||||
|
||||
``required``
|
||||
~~~~~~~~~~~~
|
||||
|
||||
By default, each ``Field`` class assumes the value is required, so if you pass
|
||||
an empty value -- either ``None`` or the empty string (``""``) -- then
|
||||
``clean()`` will raise a ``ValidationError`` exception::
|
||||
|
||||
>>> f = forms.CharField()
|
||||
>>> f.clean('foo')
|
||||
u'foo'
|
||||
>>> f.clean('')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'This field is required.']
|
||||
>>> f.clean(None)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'This field is required.']
|
||||
>>> f.clean(' ')
|
||||
u' '
|
||||
>>> f.clean(0)
|
||||
u'0'
|
||||
>>> f.clean(True)
|
||||
u'True'
|
||||
>>> f.clean(False)
|
||||
u'False'
|
||||
|
||||
To specify that a field is *not* required, pass ``required=False`` to the
|
||||
``Field`` constructor::
|
||||
|
||||
>>> f = forms.CharField(required=False)
|
||||
>>> f.clean('foo')
|
||||
u'foo'
|
||||
>>> f.clean('')
|
||||
u''
|
||||
>>> f.clean(None)
|
||||
u''
|
||||
>>> f.clean(0)
|
||||
u'0'
|
||||
>>> f.clean(True)
|
||||
u'True'
|
||||
>>> f.clean(False)
|
||||
u'False'
|
||||
|
||||
If a ``Field`` has ``required=False`` and you pass ``clean()`` an empty value,
|
||||
then ``clean()`` will return a *normalized* empty value rather than raising
|
||||
``ValidationError``. For ``CharField``, this will be a Unicode empty string.
|
||||
For other ``Field`` classes, it might be ``None``. (This varies from field to
|
||||
field.)
|
||||
|
||||
``label``
|
||||
~~~~~~~~~
|
||||
|
||||
The ``label`` argument lets you specify the "human-friendly" label for this
|
||||
field. This is used when the ``Field`` is displayed in a ``Form``.
|
||||
|
||||
As explained in _`Outputting forms as HTML` above, the default label for a
|
||||
``Field`` is generated from the field name by converting all underscores to
|
||||
spaces and upper-casing the first letter. Specify ``label`` if that default
|
||||
behavior doesn't result in an adequate label.
|
||||
|
||||
Here's a full example ``Form`` that implements ``label`` for two of its fields.
|
||||
We've specified ``auto_id=False`` to simplify the output::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField(label='Your name')
|
||||
... url = forms.URLField(label='Your Web site', required=False)
|
||||
... comment = forms.CharField()
|
||||
>>> f = CommentForm(auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Your name:</th><td><input type="text" name="name" /></td></tr>
|
||||
<tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
|
||||
``initial``
|
||||
~~~~~~~~~~~
|
||||
|
||||
The ``initial`` argument lets you specify the initial value to use when
|
||||
rendering this ``Field`` in an unbound ``Form``.
|
||||
|
||||
The use-case for this is when you want to display an "empty" form in which a
|
||||
field is initialized to a particular value. For example::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField(initial='Your name')
|
||||
... url = forms.URLField(initial='http://')
|
||||
... comment = forms.CharField()
|
||||
>>> f = CommentForm(auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
|
||||
<tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
|
||||
You may be thinking, why not just pass a dictionary of the initial values as
|
||||
data when displaying the form? Well, if you do that, you'll trigger validation,
|
||||
and the HTML output will include any validation errors::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField()
|
||||
... url = forms.URLField()
|
||||
... comment = forms.CharField()
|
||||
>>> default_data = {'name': 'Your name', 'url': 'http://'}
|
||||
>>> f = CommentForm(default_data, auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
|
||||
<tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr>
|
||||
<tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr>
|
||||
|
||||
This is why ``initial`` values are only displayed for unbound forms. For bound
|
||||
forms, the HTML output will use the bound data.
|
||||
|
||||
Also note that ``initial`` values are *not* used as "fallback" data in
|
||||
validation if a particular field's value is not given. ``initial`` values are
|
||||
*only* intended for initial form display::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField(initial='Your name')
|
||||
... url = forms.URLField(initial='http://')
|
||||
... comment = forms.CharField()
|
||||
>>> data = {'name': '', 'url': '', 'comment': 'Foo'}
|
||||
>>> f = CommentForm(data)
|
||||
>>> f.is_valid()
|
||||
False
|
||||
# The form does *not* fall back to using the initial values.
|
||||
>>> f.errors
|
||||
{'url': [u'This field is required.'], 'name': [u'This field is required.']}
|
||||
|
||||
``widget``
|
||||
~~~~~~~~~~
|
||||
|
||||
The ``widget`` argument lets you specify a ``Widget`` class to use when
|
||||
rendering this ``Field``. See _`Widgets` below for more information.
|
||||
|
||||
``help_text``
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The ``help_text`` argument lets you specify descriptive text for this
|
||||
``Field``. If you provide ``help_text``, it will be displayed next to the
|
||||
``Field`` when the ``Field`` is rendered in a ``Form``.
|
||||
|
||||
Here's a full example ``Form`` that implements ``help_text`` for two of its
|
||||
fields. We've specified ``auto_id=False`` to simplify the output::
|
||||
|
||||
>>> class HelpTextContactForm(forms.Form):
|
||||
... subject = forms.CharField(max_length=100, help_text='100 characters max.')
|
||||
... message = forms.CharField()
|
||||
... sender = forms.EmailField(help_text='A valid e-mail address, please.')
|
||||
... cc_myself = forms.BooleanField()
|
||||
>>> f = HelpTextContactForm(auto_id=False)
|
||||
>>> print f.as_table()
|
||||
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr>
|
||||
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
|
||||
<tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid e-mail address, please.</td></tr>
|
||||
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</li>
|
||||
<li>Message: <input type="text" name="message" /></li>
|
||||
<li>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</li>
|
||||
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
|
||||
>>> print f.as_p()
|
||||
<p>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</p>
|
||||
<p>Message: <input type="text" name="message" /></p>
|
||||
<p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p>
|
||||
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
|
||||
|
||||
Dynamic initial values
|
||||
----------------------
|
||||
|
||||
The ``initial`` argument to ``Field`` (explained above) lets you hard-code the
|
||||
initial value for a ``Field`` -- but what if you want to declare the initial
|
||||
value at runtime? For example, you might want to fill in a ``username`` field
|
||||
with the username of the current session.
|
||||
|
||||
To accomplish this, use the ``initial`` argument to a ``Form``. This argument,
|
||||
if given, should be a dictionary mapping field names to initial values. Only
|
||||
include the fields for which you're specifying an initial value; it's not
|
||||
necessary to include every field in your form. For example::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField()
|
||||
... url = forms.URLField()
|
||||
... comment = forms.CharField()
|
||||
>>> f = CommentForm(initial={'name': 'your username'}, auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="your username" /></td></tr>
|
||||
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
>>> f = CommentForm(initial={'name': 'another username'}, auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="another username" /></td></tr>
|
||||
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
|
||||
Just like the ``initial`` parameter to ``Field``, these values are only
|
||||
displayed for unbound forms, and they're not used as fallback values if a
|
||||
particular value isn't provided.
|
||||
|
||||
Finally, note that if a ``Field`` defines ``initial`` *and* you include
|
||||
``initial`` when instantiating the ``Form``, then the latter ``initial`` will
|
||||
have precedence. In this example, ``initial`` is provided both at the field
|
||||
level and at the form instance level, and the latter gets precedence::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField(initial='class')
|
||||
... url = forms.URLField()
|
||||
... comment = forms.CharField()
|
||||
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="instance" /></td></tr>
|
||||
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
|
||||
More coming soon
|
||||
================
|
||||
@ -77,3 +825,9 @@ That's all the documentation for now. For more, see the file
|
||||
http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py
|
||||
-- the unit tests for ``django.newforms``. This can give you a good idea of
|
||||
what's possible.
|
||||
|
||||
If you're really itching to learn and use this library, please be patient.
|
||||
We're working hard on finishing both the code and documentation.
|
||||
|
||||
Widgets
|
||||
=======
|
||||
|
@ -24,7 +24,7 @@ and Django's ``HttpResponse`` objects are file-like objects.
|
||||
|
||||
For more information on the CSV library, see the `CSV library docs`_.
|
||||
|
||||
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/
|
||||
.. _Request and response objects: ../request_response/
|
||||
.. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html
|
||||
|
||||
Here's an example::
|
||||
@ -115,5 +115,5 @@ a line of CSV for each row. It uses the `addslashes template filter`_ to ensure
|
||||
there aren't any problems with quotes. If you can be certain your data doesn't
|
||||
have single or double quotes in it, you can remove the ``addslashes`` filters.
|
||||
|
||||
.. _Django template system: http://www.djangoproject.com/documentation/templates/
|
||||
.. _addslashes template filter: http://www.djangoproject.com/documentation/templates/#addslashes
|
||||
.. _Django template system: ../templates/
|
||||
.. _addslashes template filter: ../templates/#addslashes
|
||||
|
@ -43,7 +43,7 @@ objects.
|
||||
For more information on ``HttpResponse`` objects, see
|
||||
`Request and response objects`_.
|
||||
|
||||
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/
|
||||
.. _Request and response objects: ../request_response/
|
||||
|
||||
Here's a "Hello World" example::
|
||||
|
||||
|
@ -11,7 +11,7 @@ understand how Django works, but this isn't intended to be a tutorial or
|
||||
reference. Please see our more-detailed Django documentation_ when you're ready
|
||||
to start a project.
|
||||
|
||||
.. _documentation: http://www.djangoproject.com/documentation/
|
||||
.. _documentation: ../
|
||||
|
||||
Design your model
|
||||
=================
|
||||
|
@ -15,8 +15,8 @@ To install the redirects app, follow these steps:
|
||||
to your MIDDLEWARE_CLASSES_ setting.
|
||||
3. Run the command ``manage.py syncdb``.
|
||||
|
||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
||||
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes
|
||||
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||
.. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
|
||||
|
||||
How it works
|
||||
============
|
||||
@ -46,8 +46,8 @@ resort.
|
||||
|
||||
For more on middleware, read the `middleware docs`_.
|
||||
|
||||
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id
|
||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _SITE_ID: ../settings/#site-id
|
||||
.. _middleware docs: ../middleware/
|
||||
|
||||
How to add, change and delete redirects
|
||||
=======================================
|
||||
@ -63,9 +63,9 @@ Via the Python API
|
||||
------------------
|
||||
|
||||
Redirects are represented by a standard `Django model`_, which lives in
|
||||
`django/contrib/redirects/models/redirects.py`_. You can access redirect
|
||||
`django/contrib/redirects/models.py`_. You can access redirect
|
||||
objects via the `Django database API`_.
|
||||
|
||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _django/contrib/redirects/models/redirects.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models/redirects.py
|
||||
.. _Django database API: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _Django model: ../model_api/
|
||||
.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
|
||||
.. _Django database API: ../db_api/
|
||||
|
@ -117,14 +117,14 @@ All attributes except ``session`` should be considered read-only.
|
||||
``AuthenticationMiddleware`` activated. For more, see
|
||||
`Authentication in Web requests`_.
|
||||
|
||||
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests
|
||||
.. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
|
||||
|
||||
``session``
|
||||
A readable-and-writable, dictionary-like object that represents the current
|
||||
session. This is only available if your Django installation has session
|
||||
support activated. See the `session documentation`_ for full details.
|
||||
|
||||
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _`session documentation`: ../sessions/
|
||||
|
||||
``raw_post_data``
|
||||
The raw HTTP POST data. This is only useful for advanced processing. Use
|
||||
|
@ -27,7 +27,7 @@ If you don't want to use sessions, you might as well remove the
|
||||
``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
|
||||
from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
|
||||
|
||||
.. _middleware: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _middleware: ../middleware/
|
||||
|
||||
Using sessions in views
|
||||
=======================
|
||||
@ -217,6 +217,23 @@ browser-length cookies -- cookies that expire as soon as the user closes his or
|
||||
her browser. Use this if you want people to have to log in every time they open
|
||||
a browser.
|
||||
|
||||
Clearing the session table
|
||||
==========================
|
||||
|
||||
Note that session data can accumulate in the ``django_session`` database table
|
||||
and Django does *not* provide automatic purging. Therefore, it's your job to
|
||||
purge expired sessions on a regular basis.
|
||||
|
||||
To understand this problem, consider what happens when a user uses a session.
|
||||
When a user logs in, Django adds a row to the ``django_session`` database
|
||||
table. Django updates this row each time the session data changes. If the user
|
||||
logs out manually, Django deletes the row. But if the user does *not* log out,
|
||||
the row never gets deleted.
|
||||
|
||||
Django provides a sample clean-up script in ``django/bin/daily_cleanup.py``.
|
||||
That script deletes any session in the session table whose ``expire_date`` is
|
||||
in the past -- but your application may have different requirements.
|
||||
|
||||
Settings
|
||||
========
|
||||
|
||||
@ -273,7 +290,7 @@ Whether to save the session data on every request. If this is ``False``
|
||||
(default), then the session data will only be saved if it has been modified --
|
||||
that is, if any of its dictionary values have been assigned or deleted.
|
||||
|
||||
.. _Django settings: http://www.djangoproject.com/documentation/settings/
|
||||
.. _Django settings: ../settings/
|
||||
|
||||
Technical details
|
||||
=================
|
||||
|
@ -59,7 +59,7 @@ Use the ``--settings`` command-line argument to specify the settings manually::
|
||||
|
||||
django-admin.py runserver --settings=mysite.settings
|
||||
|
||||
.. _django-admin.py: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _django-admin.py: ../django_admin/
|
||||
|
||||
On the server (mod_python)
|
||||
--------------------------
|
||||
@ -75,7 +75,7 @@ settings file to use. Do that with ``SetEnv``::
|
||||
|
||||
Read the `Django mod_python documentation`_ for more information.
|
||||
|
||||
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/
|
||||
.. _Django mod_python documentation: ../modpython/
|
||||
|
||||
Default settings
|
||||
================
|
||||
@ -102,7 +102,7 @@ between the current settings file and Django's default settings.
|
||||
|
||||
For more, see the `diffsettings documentation`_.
|
||||
|
||||
.. _diffsettings documentation: http://www.djangoproject.com/documentation/django_admin/#diffsettings
|
||||
.. _diffsettings documentation: ../django_admin/#diffsettings
|
||||
|
||||
Using settings in Python code
|
||||
=============================
|
||||
@ -157,13 +157,13 @@ ABSOLUTE_URL_OVERRIDES
|
||||
|
||||
Default: ``{}`` (Empty dictionary)
|
||||
|
||||
A dictionary mapping ``"app_label.module_name"`` strings to functions that take
|
||||
A dictionary mapping ``"app_label.model_name"`` strings to functions that take
|
||||
a model object and return its URL. This is a way of overriding
|
||||
``get_absolute_url()`` methods on a per-installation basis. Example::
|
||||
|
||||
ABSOLUTE_URL_OVERRIDES = {
|
||||
'blogs.blogs': lambda o: "/blogs/%s/" % o.slug,
|
||||
'news.stories': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
|
||||
'blogs.Weblog': lambda o: "/blogs/%s/" % o.slug,
|
||||
'news.Story': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
|
||||
}
|
||||
|
||||
ADMIN_FOR
|
||||
@ -306,7 +306,7 @@ pages -- and, possibly, by other parts of the system. See
|
||||
|
||||
See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
||||
|
||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
||||
.. _allowed date format strings: ../templates/#now
|
||||
|
||||
DATETIME_FORMAT
|
||||
---------------
|
||||
@ -319,7 +319,7 @@ pages -- and, possibly, by other parts of the system. See
|
||||
|
||||
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
||||
|
||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
||||
.. _allowed date format strings: ../templates/#now
|
||||
|
||||
DEBUG
|
||||
-----
|
||||
@ -433,7 +433,7 @@ A tuple of strings designating all applications that are enabled in this Django
|
||||
installation. Each string should be a full Python path to a Python package that
|
||||
contains a Django application, as created by `django-admin.py startapp`_.
|
||||
|
||||
.. _django-admin.py startapp: http://www.djangoproject.com/documentation/django_admin/#startapp-appname
|
||||
.. _django-admin.py startapp: ../django_admin/#startapp-appname
|
||||
|
||||
INTERNAL_IPS
|
||||
------------
|
||||
@ -464,7 +464,7 @@ A string representing the language code for this installation. This should be
|
||||
in standard language format. For example, U.S. English is ``"en-us"``. See the
|
||||
`internationalization docs`_.
|
||||
|
||||
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _internationalization docs: ../i18n/
|
||||
|
||||
LANGUAGES
|
||||
---------
|
||||
@ -557,6 +557,11 @@ Default: ``''`` (Empty string)
|
||||
URL that handles the media served from ``MEDIA_ROOT``.
|
||||
Example: ``"http://media.lawrence.com"``
|
||||
|
||||
Note that this should have a trailing slash if it has a path component.
|
||||
|
||||
Good: ``"http://www.example.com/static/"``
|
||||
Bad: ``"http://www.example.com/static"``
|
||||
|
||||
MIDDLEWARE_CLASSES
|
||||
------------------
|
||||
|
||||
@ -612,7 +617,7 @@ Default: Not defined
|
||||
A string representing the full Python import path to your root URLconf. For example:
|
||||
``"mydjangoapps.urls"``. See `How Django processes a request`_.
|
||||
|
||||
.. _How Django processes a request: http://www.djangoproject.com/documentation/url_dispatch/#how-django-processes-a-request
|
||||
.. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request
|
||||
|
||||
SECRET_KEY
|
||||
----------
|
||||
@ -704,7 +709,7 @@ and a single database can manage content for multiple sites.
|
||||
|
||||
See the `site framework docs`_.
|
||||
|
||||
.. _site framework docs: http://www.djangoproject.com/documentation/sites/
|
||||
.. _site framework docs: ../sites/
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS
|
||||
---------------------------
|
||||
@ -760,7 +765,7 @@ Default: ``''`` (Empty string)
|
||||
Output, as a string, that the template system should use for invalid (e.g.
|
||||
misspelled) variables. See `How invalid variables are handled`_.
|
||||
|
||||
.. _How invalid variables are handled: http://www.djangoproject.com/documentation/templates_python/#how-invalid-variables-are-handled
|
||||
.. _How invalid variables are handled: ../templates_python/#how-invalid-variables-are-handled
|
||||
|
||||
TEST_RUNNER
|
||||
-----------
|
||||
@ -798,7 +803,7 @@ pages -- and, possibly, by other parts of the system. See
|
||||
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and
|
||||
MONTH_DAY_FORMAT.
|
||||
|
||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
||||
.. _allowed date format strings: ../templates/#now
|
||||
|
||||
TIME_ZONE
|
||||
---------
|
||||
@ -868,11 +873,11 @@ Different locales have different formats. For example, U.S. English would say
|
||||
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
|
||||
TIME_FORMAT and MONTH_DAY_FORMAT.
|
||||
|
||||
.. _cache docs: http://www.djangoproject.com/documentation/cache/
|
||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _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
|
||||
.. _template documentation: http://www.djangoproject.com/documentation/templates_python/
|
||||
.. _cache docs: ../cache/
|
||||
.. _middleware docs: ../middleware/
|
||||
.. _session docs: ../sessions/
|
||||
.. _See available choices: http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||
.. _template documentation: ../templates_python/
|
||||
|
||||
Creating your own settings
|
||||
==========================
|
||||
|
@ -23,8 +23,8 @@ you express this information in Python code.
|
||||
It works much like Django's `syndication framework`_. To create a sitemap, just
|
||||
write a ``Sitemap`` class and point to it in your URLconf_.
|
||||
|
||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _syndication framework: ../syndication/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Installation
|
||||
============
|
||||
@ -41,9 +41,9 @@ To install the sitemap app, follow these steps:
|
||||
reason it needs to go into ``INSTALLED_APPS`` is so that the
|
||||
``load_template_source`` template loader can find the default templates.)
|
||||
|
||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
||||
.. _TEMPLATE_LOADERS: http://www.djangoproject.com/documentation/settings/#template-loaders
|
||||
.. _sites framework: http://www.djangoproject.com/documentation/sites/
|
||||
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||
.. _TEMPLATE_LOADERS: ../settings/#template-loaders
|
||||
.. _sites framework: ../sites/
|
||||
|
||||
Initialization
|
||||
==============
|
||||
@ -68,7 +68,7 @@ The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
|
||||
``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
|
||||
(e.g., ``BlogSitemap(some_var)``).
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Sitemap classes
|
||||
===============
|
||||
@ -217,8 +217,8 @@ defined for the current ``SITE_ID`` (see the `sites documentation`_) and
|
||||
creates an entry in the sitemap. These entries include only the ``location``
|
||||
attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
|
||||
|
||||
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
|
||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
||||
.. _flatpages: ../flatpages/
|
||||
.. _sites documentation: ../sites/
|
||||
|
||||
``GenericSitemap``
|
||||
------------------
|
||||
@ -232,7 +232,7 @@ the ``lastmod`` attribute in the generated sitemap. You may also pass
|
||||
``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
|
||||
constructor to specify these attributes for all URLs.
|
||||
|
||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _generic views: ../generic_views/
|
||||
|
||||
Example
|
||||
-------
|
||||
@ -261,7 +261,7 @@ Here's an example of a URLconf_ using both::
|
||||
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
||||
)
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Creating a sitemap index
|
||||
========================
|
||||
|
@ -276,8 +276,8 @@ you want your admin site to have access to all objects (not just site-specific
|
||||
ones), put ``objects = models.Manager()`` in your model, before you define
|
||||
``CurrentSiteManager``.
|
||||
|
||||
.. _manager: http://www.djangoproject.com/documentation/model_api/#managers
|
||||
.. _manager documentation: http://www.djangoproject.com/documentation/model_api/#managers
|
||||
.. _manager: ../model_api/#managers
|
||||
.. _manager documentation: ../model_api/#managers
|
||||
|
||||
How Django uses the sites framework
|
||||
===================================
|
||||
@ -316,7 +316,7 @@ Here's how Django uses the sites framework:
|
||||
* The shortcut view (``django.views.defaults.shortcut``) uses the domain of
|
||||
the current ``Site`` object when calculating an object's URL.
|
||||
|
||||
.. _redirects framework: http://www.djangoproject.com/documentation/redirects/
|
||||
.. _flatpages framework: http://www.djangoproject.com/documentation/flatpages/
|
||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _authentication framework: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _redirects framework: ../redirects/
|
||||
.. _flatpages framework: ../flatpages/
|
||||
.. _syndication framework: ../syndication/
|
||||
.. _authentication framework: ../authentication/
|
||||
|
@ -24,7 +24,7 @@ production setting. Use this only for development.
|
||||
For information on serving static files in an Apache production environment,
|
||||
see the `Django mod_python documentation`_.
|
||||
|
||||
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/#serving-media-files
|
||||
.. _Django mod_python documentation: ../modpython/#serving-media-files
|
||||
|
||||
How to do it
|
||||
============
|
||||
@ -49,7 +49,7 @@ Examples:
|
||||
* The file ``/path/bar.jpg`` will not be accessible, because it doesn't
|
||||
fall under the document root.
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Directory listings
|
||||
==================
|
||||
@ -122,4 +122,4 @@ associated with the ``django.views.static.serve`` view. If not
|
||||
Of course, the catch here is that you'll have to remember to set ``DEBUG=False``
|
||||
in your production settings file. But you should be doing that anyway.
|
||||
|
||||
.. _DEBUG setting: http://www.djangoproject.com/documentation/settings/#debug
|
||||
.. _DEBUG setting: ../settings/#debug
|
||||
|
@ -26,7 +26,7 @@ to determine which feed to output.
|
||||
|
||||
To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Initialization
|
||||
--------------
|
||||
@ -72,8 +72,8 @@ The above example registers two feeds:
|
||||
|
||||
Once that's set up, you just need to define the ``Feed`` classes themselves.
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
.. _settings file: ../settings/
|
||||
|
||||
Feed classes
|
||||
------------
|
||||
@ -156,8 +156,8 @@ put into those elements.
|
||||
{{ obj.description }}
|
||||
|
||||
.. _chicagocrime.org: http://www.chicagocrime.org/
|
||||
.. _object-relational mapper: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _Django templates: http://www.djangoproject.com/documentation/templates/
|
||||
.. _object-relational mapper: ../db_api/
|
||||
.. _Django templates: ../templates/
|
||||
|
||||
A complex example
|
||||
-----------------
|
||||
@ -277,7 +277,7 @@ Feeds created by the syndication framework automatically include the
|
||||
appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
|
||||
comes directly from your `LANGUAGE_CODE setting`_.
|
||||
|
||||
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code
|
||||
.. _LANGUAGE_CODE setting: ../settings/#language-code
|
||||
|
||||
URLs
|
||||
----
|
||||
@ -292,7 +292,7 @@ Atom feeds require a ``<link rel="self">`` that defines the feed's current
|
||||
location. The syndication framework populates this automatically, using the
|
||||
domain of the current site according to the SITE_ID setting.
|
||||
|
||||
.. _SITE_ID setting: http://www.djangoproject.com/documentation/settings/#site-id
|
||||
.. _SITE_ID setting: ../settings/#site-id
|
||||
|
||||
Publishing Atom and RSS feeds in tandem
|
||||
---------------------------------------
|
||||
|
@ -792,7 +792,7 @@ Note that if you use ``{% ssi %}``, you'll need to define
|
||||
|
||||
See also: ``{% include %}``.
|
||||
|
||||
.. _ALLOWED_INCLUDE_ROOTS: http://www.djangoproject.com/documentation/settings/#allowed-include-roots
|
||||
.. _ALLOWED_INCLUDE_ROOTS: ../settings/#allowed-include-roots
|
||||
|
||||
templatetag
|
||||
~~~~~~~~~~~
|
||||
@ -924,13 +924,31 @@ Replaces ampersands with ``&`` entities.
|
||||
floatformat
|
||||
~~~~~~~~~~~
|
||||
|
||||
Rounds a floating-point number to one decimal place -- but only if there's a
|
||||
decimal part to be displayed. For example:
|
||||
When used without an argument, rounds a floating-point number to one decimal
|
||||
place -- but only if there's a decimal part to be displayed. For example:
|
||||
|
||||
* ``36.123`` gets converted to ``36.1``
|
||||
* ``36.15`` gets converted to ``36.2``
|
||||
* ``36`` gets converted to ``36``
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
If used with a numeric integer argument, ``floatformat`` rounds a number to that
|
||||
many decimal places. For example:
|
||||
|
||||
* ``36.1234`` with floatformat:3 gets converted to ``36.123``
|
||||
* ``36`` with floatformat:4 gets converted to ``36.0000``
|
||||
|
||||
If the argument passed to ``floatformat`` is negative, it will round a number to
|
||||
that many decimal places -- but only if there's a decimal part to be displayed.
|
||||
For example:
|
||||
|
||||
* ``36.1234`` with floatformat:-3 gets converted to ``36.123``
|
||||
* ``36`` with floatformat:-4 gets converted to ``36``
|
||||
|
||||
Using ``floatformat`` with no argument is equivalent to using ``floatformat`` with
|
||||
an argument of ``-1``.
|
||||
|
||||
get_digit
|
||||
~~~~~~~~~
|
||||
|
||||
@ -1200,7 +1218,7 @@ django.contrib.humanize
|
||||
A set of Django template filters useful for adding a "human touch" to data. See
|
||||
the `humanize documentation`_.
|
||||
|
||||
.. _humanize documentation: http://www.djangoproject.com/documentation/add_ons/#humanize
|
||||
.. _humanize documentation: ../add_ons/#humanize
|
||||
|
||||
django.contrib.markup
|
||||
---------------------
|
||||
|
@ -11,7 +11,7 @@ If you're looking to use the Django template system as part of another
|
||||
application -- i.e., without the rest of the framework -- make sure to read
|
||||
the `configuration`_ section later in this document.
|
||||
|
||||
.. _`The Django template language: For template authors`: http://www.djangoproject.com/documentation/templates/
|
||||
.. _`The Django template language: For template authors`: ../templates/
|
||||
|
||||
Basics
|
||||
======
|
||||
@ -327,8 +327,8 @@ Note::
|
||||
|
||||
Here's what each of the default processors does:
|
||||
|
||||
.. _HttpRequest object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
||||
.. _TEMPLATE_CONTEXT_PROCESSORS setting: http://www.djangoproject.com/documentation/settings/#template-context-processors
|
||||
.. _HttpRequest object: ../request_response/#httprequest-objects
|
||||
.. _TEMPLATE_CONTEXT_PROCESSORS setting: ../settings/#template-context-processors
|
||||
|
||||
django.core.context_processors.auth
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -353,9 +353,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
||||
permissions that the currently logged-in user has. See the `permissions
|
||||
docs`_.
|
||||
|
||||
.. _user authentication docs: http://www.djangoproject.com/documentation/authentication/#users
|
||||
.. _message docs: http://www.djangoproject.com/documentation/authentication/#messages
|
||||
.. _permissions docs: http://www.djangoproject.com/documentation/authentication/#permissions
|
||||
.. _user authentication docs: ../authentication/#users
|
||||
.. _message docs: ../authentication/#messages
|
||||
.. _permissions docs: ../authentication/#permissions
|
||||
|
||||
django.core.context_processors.debug
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -383,9 +383,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
||||
|
||||
See the `internationalization docs`_ for more.
|
||||
|
||||
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
|
||||
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code
|
||||
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _LANGUAGES setting: ../settings/#languages
|
||||
.. _LANGUAGE_CODE setting: ../settings/#language-code
|
||||
.. _internationalization docs: ../i18n/
|
||||
|
||||
django.core.context_processors.request
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -944,7 +944,7 @@ The ``takes_context`` parameter defaults to ``False``. When it's set to *True*,
|
||||
the tag is passed the context object, as in this example. That's the only
|
||||
difference between this case and the previous ``inclusion_tag`` example.
|
||||
|
||||
.. _tutorials: http://www.djangoproject.com/documentation/tutorial1/#creating-models
|
||||
.. _tutorials: ../tutorial1/#creating-models
|
||||
|
||||
Setting a variable in the context
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -1115,5 +1115,5 @@ settings you wish to specify. You might want to consider setting at least
|
||||
`settings documentation`_, and any setting starting with *TEMPLATE_*
|
||||
is of obvious interest.
|
||||
|
||||
.. _settings file: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||
.. _settings documentation: http://www.djangoproject.com/documentation/settings/
|
||||
.. _settings file: ../settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||
.. _settings documentation: ../settings/
|
||||
|
@ -13,7 +13,7 @@ changed unexpectedly as a result of the refactor.
|
||||
Testing a web application is a complex task, as there are many
|
||||
components of a web application that must be validated and tested. To
|
||||
help you test your application, Django provides a test execution
|
||||
framework, and range of utilities that can be used to stimulate and
|
||||
framework, and range of utilities that can be used to simulate and
|
||||
inspect various facets of a web application.
|
||||
|
||||
This testing framework is currently under development, and may change
|
||||
@ -220,7 +220,7 @@ can be invoked on the ``Client`` instance.
|
||||
|
||||
will result in the evaluation of a GET request equivalent to::
|
||||
|
||||
http://yoursite.com/customers/details/?name='fred'&age=7
|
||||
http://yoursite.com/customers/details/?name=fred&age=7
|
||||
|
||||
``post(path, data={})``
|
||||
Make a POST request on the provided ``path``. The key-value pairs in the
|
||||
@ -244,11 +244,11 @@ can be invoked on the ``Client`` instance.
|
||||
|
||||
``login(path, username, password)``
|
||||
In a production site, it is likely that some views will be protected with
|
||||
the @login_required URL provided by ``django.contrib.auth``. Interacting
|
||||
the @login_required decorator provided by ``django.contrib.auth``. Interacting
|
||||
with a URL that has been login protected is a slightly complex operation,
|
||||
so the Test Client provides a simple URL to automate the login process. A
|
||||
so the Test Client provides a simple method to automate the login process. A
|
||||
call to ``login()`` stimulates the series of GET and POST calls required
|
||||
to log a user into a @login_required protected URL.
|
||||
to log a user into a @login_required protected view.
|
||||
|
||||
If login is possible, the final return value of ``login()`` is the response
|
||||
that is generated by issuing a GET request on the protected URL. If login
|
||||
@ -415,7 +415,7 @@ arguments:
|
||||
tested. This is the same format returned by ``django.db.models.get_apps()``
|
||||
|
||||
Verbosity determines the amount of notification and debug information that
|
||||
will be printed to the console; '0' is no output, '1' is normal output,
|
||||
will be printed to the console; `0` is no output, `1` is normal output,
|
||||
and `2` is verbose output.
|
||||
|
||||
Testing utilities
|
||||
|
@ -17,7 +17,7 @@ installed by running the Python interactive interpreter and typing
|
||||
``import django``. If that command runs successfully, with no errors, Django is
|
||||
installed.
|
||||
|
||||
.. _`Django installed`: http://www.djangoproject.com/documentation/install/
|
||||
.. _`Django installed`: ../install/
|
||||
|
||||
Creating a project
|
||||
==================
|
||||
@ -108,7 +108,7 @@ It worked!
|
||||
|
||||
Full docs for the development server are at `django-admin documentation`_.
|
||||
|
||||
.. _django-admin documentation: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _django-admin documentation: ../django_admin/
|
||||
|
||||
Database setup
|
||||
--------------
|
||||
@ -378,7 +378,7 @@ as you like, and it will only ever create the tables that don't exist.
|
||||
Read the `django-admin.py documentation`_ for full information on what the
|
||||
``manage.py`` utility can do.
|
||||
|
||||
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _django-admin.py documentation: ../django_admin/
|
||||
|
||||
Playing with the API
|
||||
====================
|
||||
@ -555,5 +555,5 @@ For full details on the database API, see our `Database API reference`_.
|
||||
When you're comfortable with the API, read `part 2 of this tutorial`_ to get
|
||||
Django's automatic admin working.
|
||||
|
||||
.. _Database API reference: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _part 2 of this tutorial: http://www.djangoproject.com/documentation/tutorial2/
|
||||
.. _Database API reference: ../db_api/
|
||||
.. _part 2 of this tutorial: ../tutorial2/
|
||||
|
@ -5,7 +5,7 @@ Writing your first Django app, part 2
|
||||
This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll
|
||||
application and will focus on Django's automatically-generated admin site.
|
||||
|
||||
.. _Tutorial 1: http://www.djangoproject.com/documentation/tutorial1/
|
||||
.. _Tutorial 1: ../tutorial1/
|
||||
|
||||
.. admonition:: Philosophy
|
||||
|
||||
@ -64,7 +64,7 @@ tutorial, remember?) You should see the Django admin index page:
|
||||
By default, you should see two types of editable content: groups and users.
|
||||
These are core features Django ships with by default.
|
||||
|
||||
.. _"I can't log in" questions: http://www.djangoproject.com/documentation/faq/#the-admin-site
|
||||
.. _"I can't log in" questions: ../faq/#the-admin-site
|
||||
|
||||
Make the poll app modifiable in the admin
|
||||
=========================================
|
||||
@ -402,7 +402,7 @@ Django automatically looks for a ``templates/`` subdirectory within each app
|
||||
package, for use as a fallback. See the `loader types documentation`_ for full
|
||||
information.
|
||||
|
||||
.. _loader types documentation: http://www.djangoproject.com/documentation/templates_python/#loader-types
|
||||
.. _loader types documentation: ../templates_python/#loader-types
|
||||
|
||||
Customize the admin index page
|
||||
==============================
|
||||
@ -433,5 +433,5 @@ general, see the `Django admin CSS guide`_.
|
||||
When you're comfortable with the admin site, read `part 3 of this tutorial`_ to
|
||||
start working on public poll views.
|
||||
|
||||
.. _Django admin CSS guide: http://www.djangoproject.com/documentation/admin_css/
|
||||
.. _part 3 of this tutorial: http://www.djangoproject.com/documentation/tutorial3/
|
||||
.. _Django admin CSS guide: ../admin_css/
|
||||
.. _part 3 of this tutorial: ../tutorial3/
|
||||
|
@ -5,7 +5,7 @@ Writing your first Django app, part 3
|
||||
This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
|
||||
application and will focus on creating the public interface -- "views."
|
||||
|
||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
||||
.. _Tutorial 2: ../tutorial2/
|
||||
|
||||
Philosophy
|
||||
==========
|
||||
@ -117,8 +117,8 @@ time the URLconf module is loaded. They're super fast.
|
||||
|
||||
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
|
||||
.. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
|
||||
.. _request and response documentation: http://www.djangoproject.com/documentation/request_response/
|
||||
.. _URLconf documentation: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _request and response documentation: ../request_response/
|
||||
.. _URLconf documentation: ../url_dispatch/
|
||||
|
||||
Write your first view
|
||||
=====================
|
||||
@ -260,8 +260,7 @@ provides a shortcut. Here's the full ``index()`` view, rewritten::
|
||||
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
|
||||
return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
|
||||
|
||||
Note that we no longer need to import ``loader``, ``Context`` or
|
||||
``HttpResponse``.
|
||||
Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
|
||||
|
||||
The ``render_to_response()`` function takes a template name as its first
|
||||
argument and a dictionary as its optional second argument. It returns an
|
||||
@ -300,7 +299,7 @@ rewritten::
|
||||
|
||||
The ``get_object_or_404()`` function takes a Django model module as its first
|
||||
argument and an arbitrary number of keyword arguments, which it passes to the
|
||||
module's ``get_object()`` function. It raises ``Http404`` if the object doesn't
|
||||
module's ``get()`` function. It raises ``Http404`` if the object doesn't
|
||||
exist.
|
||||
|
||||
.. admonition:: Philosophy
|
||||
@ -377,7 +376,7 @@ iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
|
||||
|
||||
See the `template guide`_ for full details on how templates work.
|
||||
|
||||
.. _template guide: http://www.djangoproject.com/documentation/templates/
|
||||
.. _template guide: ../templates/
|
||||
|
||||
Simplifying the URLconfs
|
||||
========================
|
||||
@ -464,4 +463,4 @@ All the poll app cares about is its relative URLs, not its absolute URLs.
|
||||
When you're comfortable with writing views, read `part 4 of this tutorial`_ to
|
||||
learn about simple form processing and generic views.
|
||||
|
||||
.. _part 4 of this tutorial: http://www.djangoproject.com/documentation/tutorial4/
|
||||
.. _part 4 of this tutorial: ../tutorial4/
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user