mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
Merged revisions 4186 to 4454 from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/sqlalchemy@4455 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
122426e745
commit
fe361e678a
6
AUTHORS
6
AUTHORS
@ -78,6 +78,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Clint Ecker
|
Clint Ecker
|
||||||
Enrico <rico.bl@gmail.com>
|
Enrico <rico.bl@gmail.com>
|
||||||
favo@exoweb.net
|
favo@exoweb.net
|
||||||
|
Eric Floehr <eric@intellovations.com>
|
||||||
gandalf@owca.info
|
gandalf@owca.info
|
||||||
Baishampayan Ghose
|
Baishampayan Ghose
|
||||||
martin.glueck@gmail.com
|
martin.glueck@gmail.com
|
||||||
@ -118,13 +119,16 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Manuzhai
|
Manuzhai
|
||||||
Petar Marić
|
Petar Marić
|
||||||
mark@junklight.com
|
mark@junklight.com
|
||||||
|
Yasushi Masuda <whosaysni@gmail.com>
|
||||||
mattycakes@gmail.com
|
mattycakes@gmail.com
|
||||||
Jason McBrayer <http://www.carcosa.net/jason/>
|
Jason McBrayer <http://www.carcosa.net/jason/>
|
||||||
mccutchen@gmail.com
|
mccutchen@gmail.com
|
||||||
michael.mcewan@gmail.com
|
michael.mcewan@gmail.com
|
||||||
|
mitakummaa@gmail.com
|
||||||
mmarshall
|
mmarshall
|
||||||
Eric Moritz <http://eric.themoritzfamily.com/>
|
Eric Moritz <http://eric.themoritzfamily.com/>
|
||||||
Robin Munn <http://www.geekforgod.com/>
|
Robin Munn <http://www.geekforgod.com/>
|
||||||
|
Robert Myers <myer0052@gmail.com>
|
||||||
Nebojša Dorđević
|
Nebojša Dorđević
|
||||||
Fraser Nevett <mail@nevett.org>
|
Fraser Nevett <mail@nevett.org>
|
||||||
Sam Newman <http://www.magpiebrain.com/>
|
Sam Newman <http://www.magpiebrain.com/>
|
||||||
@ -149,6 +153,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
serbaut@gmail.com
|
serbaut@gmail.com
|
||||||
Pete Shinners <pete@shinners.org>
|
Pete Shinners <pete@shinners.org>
|
||||||
SmileyChris <smileychris@gmail.com>
|
SmileyChris <smileychris@gmail.com>
|
||||||
|
smurf@smurf.noris.de
|
||||||
sopel
|
sopel
|
||||||
Thomas Steinacher <tom@eggdrop.ch>
|
Thomas Steinacher <tom@eggdrop.ch>
|
||||||
nowell strite
|
nowell strite
|
||||||
@ -160,6 +165,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Tom Insam
|
Tom Insam
|
||||||
Joe Topjian <http://joe.terrarum.net/geek/code/python/django/>
|
Joe Topjian <http://joe.terrarum.net/geek/code/python/django/>
|
||||||
Karen Tracey <graybark@bellsouth.net>
|
Karen Tracey <graybark@bellsouth.net>
|
||||||
|
Makoto Tsuyuki <mtsuyuki@gmail.com>
|
||||||
Amit Upadhyay
|
Amit Upadhyay
|
||||||
Geert Vanderkelen
|
Geert Vanderkelen
|
||||||
Milton Waddams
|
Milton Waddams
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Daily cleanup job.
|
Daily cleanup job.
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ ADMINS = ()
|
|||||||
INTERNAL_IPS = ()
|
INTERNAL_IPS = ()
|
||||||
|
|
||||||
# Local time zone for this installation. All choices can be found here:
|
# Local time zone for this installation. All choices can be found here:
|
||||||
# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||||
TIME_ZONE = 'America/Chicago'
|
TIME_ZONE = 'America/Chicago'
|
||||||
|
|
||||||
# Language code for this installation. All choices can be found here:
|
# Language code for this installation. All choices can be found here:
|
||||||
|
@ -17,7 +17,7 @@ DATABASE_HOST = '' # Set to empty string for localhost. Not used wit
|
|||||||
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
||||||
|
|
||||||
# Local time zone for this installation. All choices can be found here:
|
# Local time zone for this installation. All choices can be found here:
|
||||||
# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||||
TIME_ZONE = 'America/Chicago'
|
TIME_ZONE = 'America/Chicago'
|
||||||
|
|
||||||
# Language code for this installation. All choices can be found here:
|
# Language code for this installation. All choices can be found here:
|
||||||
|
@ -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 %}">
|
<div id="content" class="{% block coltype %}colM{% endblock %}">
|
||||||
{% block pretitle %}{% endblock %}
|
{% block pretitle %}{% endblock %}
|
||||||
{% block content_title %}{% if title %}<h1>{{ title|escape }}</h1>{% endif %}{% endblock %}
|
{% block content_title %}{% if title %}<h1>{{ title|escape }}</h1>{% endif %}{% endblock %}
|
||||||
{% block content %}{{ content }}{% endblock %}
|
{% block content %}
|
||||||
|
{% block object-tools %}{% endblock %}
|
||||||
|
{{ content }}
|
||||||
|
{% endblock %}
|
||||||
{% block sidebar %}{% endblock %}
|
{% block sidebar %}{% endblock %}
|
||||||
<br class="clear" />
|
<br class="clear" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,11 +16,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}{% endblock %}
|
{% endif %}{% endblock %}
|
||||||
{% block content %}<div id="content-main">
|
{% block content %}<div id="content-main">
|
||||||
|
{% block object-tools %}
|
||||||
{% if change %}{% if not is_popup %}
|
{% if change %}{% if not is_popup %}
|
||||||
<ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
|
<ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
|
||||||
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}{% endif %}
|
{% endif %}{% endif %}
|
||||||
|
{% endblock %}
|
||||||
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
|
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
|
||||||
<div>
|
<div>
|
||||||
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
|
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
{% block coltype %}flex{% endblock %}
|
{% block coltype %}flex{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="content-main">
|
<div id="content-main">
|
||||||
|
{% block object-tools %}
|
||||||
{% if has_add_permission %}
|
{% if has_add_permission %}
|
||||||
<ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
|
<ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
|
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
|
||||||
{% block search %}{% search_form cl %}{% endblock %}
|
{% block search %}{% search_form cl %}{% endblock %}
|
||||||
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}
|
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}
|
||||||
|
@ -101,6 +101,10 @@ def result_headers(cl):
|
|||||||
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
||||||
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
|
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
|
||||||
|
|
||||||
|
def _boolean_icon(field_val):
|
||||||
|
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||||
|
return '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
||||||
|
|
||||||
def items_for_result(cl, result):
|
def items_for_result(cl, result):
|
||||||
first = True
|
first = True
|
||||||
pk = cl.lookup_opts.pk.attname
|
pk = cl.lookup_opts.pk.attname
|
||||||
@ -114,8 +118,13 @@ def items_for_result(cl, result):
|
|||||||
try:
|
try:
|
||||||
attr = getattr(result, field_name)
|
attr = getattr(result, field_name)
|
||||||
allow_tags = getattr(attr, 'allow_tags', False)
|
allow_tags = getattr(attr, 'allow_tags', False)
|
||||||
|
boolean = getattr(attr, 'boolean', False)
|
||||||
if callable(attr):
|
if callable(attr):
|
||||||
attr = attr()
|
attr = attr()
|
||||||
|
if boolean:
|
||||||
|
allow_tags = True
|
||||||
|
result_repr = _boolean_icon(attr)
|
||||||
|
else:
|
||||||
result_repr = str(attr)
|
result_repr = str(attr)
|
||||||
except (AttributeError, ObjectDoesNotExist):
|
except (AttributeError, ObjectDoesNotExist):
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
@ -147,8 +156,7 @@ def items_for_result(cl, result):
|
|||||||
row_class = ' class="nowrap"'
|
row_class = ' class="nowrap"'
|
||||||
# Booleans are special: We use images.
|
# Booleans are special: We use images.
|
||||||
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
|
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
|
||||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
result_repr = _boolean_icon(field_val)
|
||||||
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
|
||||||
# FloatFields are special: Zero-pad the decimals.
|
# FloatFields are special: Zero-pad the decimals.
|
||||||
elif isinstance(f, models.FloatField):
|
elif isinstance(f, models.FloatField):
|
||||||
if field_val is not None:
|
if field_val is not None:
|
||||||
|
@ -29,6 +29,8 @@ urlpatterns = patterns('',
|
|||||||
|
|
||||||
# "Add user" -- a special-case view
|
# "Add user" -- a special-case view
|
||||||
('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
|
('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
|
||||||
|
# "Change user password" -- another special-case view
|
||||||
|
('^auth/user/(\d+)/password/$', 'django.contrib.admin.views.auth.user_change_password'),
|
||||||
|
|
||||||
# Add/change/delete/history
|
# Add/change/delete/history
|
||||||
('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'),
|
('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'),
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
from django.contrib.auth.forms import UserCreationForm
|
from django.contrib.auth.forms import UserCreationForm, AdminPasswordChangeForm
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django import forms, template
|
from django import oldforms, template
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response, get_object_or_404
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.utils.html import escape
|
||||||
|
|
||||||
def user_add_stage(request):
|
def user_add_stage(request):
|
||||||
if not request.user.has_perm('auth.change_user'):
|
if not request.user.has_perm('auth.change_user'):
|
||||||
@ -24,7 +25,7 @@ def user_add_stage(request):
|
|||||||
return HttpResponseRedirect('../%s/' % new_user.id)
|
return HttpResponseRedirect('../%s/' % new_user.id)
|
||||||
else:
|
else:
|
||||||
errors = new_data = {}
|
errors = new_data = {}
|
||||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||||
return render_to_response('admin/auth/user/add_form.html', {
|
return render_to_response('admin/auth/user/add_form.html', {
|
||||||
'title': _('Add user'),
|
'title': _('Add user'),
|
||||||
'form': form,
|
'form': form,
|
||||||
@ -42,3 +43,35 @@ def user_add_stage(request):
|
|||||||
'username_help_text': User._meta.get_field('username').help_text,
|
'username_help_text': User._meta.get_field('username').help_text,
|
||||||
}, context_instance=template.RequestContext(request))
|
}, context_instance=template.RequestContext(request))
|
||||||
user_add_stage = staff_member_required(user_add_stage)
|
user_add_stage = staff_member_required(user_add_stage)
|
||||||
|
|
||||||
|
def user_change_password(request, id):
|
||||||
|
if not request.user.has_perm('auth.change_user'):
|
||||||
|
raise PermissionDenied
|
||||||
|
user = get_object_or_404(User, pk=id)
|
||||||
|
manipulator = AdminPasswordChangeForm(user)
|
||||||
|
if request.method == 'POST':
|
||||||
|
new_data = request.POST.copy()
|
||||||
|
errors = manipulator.get_validation_errors(new_data)
|
||||||
|
if not errors:
|
||||||
|
new_user = manipulator.save(new_data)
|
||||||
|
msg = _('Password changed successfully.')
|
||||||
|
request.user.message_set.create(message=msg)
|
||||||
|
return HttpResponseRedirect('..')
|
||||||
|
else:
|
||||||
|
errors = new_data = {}
|
||||||
|
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||||
|
return render_to_response('admin/auth/user/change_password.html', {
|
||||||
|
'title': _('Change password: %s') % escape(user.username),
|
||||||
|
'form': form,
|
||||||
|
'is_popup': request.REQUEST.has_key('_popup'),
|
||||||
|
'add': True,
|
||||||
|
'change': False,
|
||||||
|
'has_delete_permission': False,
|
||||||
|
'has_change_permission': True,
|
||||||
|
'has_absolute_url': False,
|
||||||
|
'first_form_field_id': 'id_password1',
|
||||||
|
'opts': User._meta,
|
||||||
|
'original': user,
|
||||||
|
'show_save': True,
|
||||||
|
}, context_instance=template.RequestContext(request))
|
||||||
|
user_change_password = staff_member_required(user_change_password)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from django import forms, template
|
from django import oldforms, template
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.admin.filterspecs import FilterSpec
|
from django.contrib.admin.filterspecs import FilterSpec
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
@ -46,8 +46,8 @@ def quote(s):
|
|||||||
"""
|
"""
|
||||||
Ensure that primary key values do not confuse the admin URLs by escaping
|
Ensure that primary key values do not confuse the admin URLs by escaping
|
||||||
any '/', '_' and ':' characters. Similar to urllib.quote, except that the
|
any '/', '_' and ':' characters. Similar to urllib.quote, except that the
|
||||||
quoting is slightly different so that it doesn't get autoamtically
|
quoting is slightly different so that it doesn't get automatically
|
||||||
unquoted by the web browser.
|
unquoted by the Web browser.
|
||||||
"""
|
"""
|
||||||
if type(s) != type(''):
|
if type(s) != type(''):
|
||||||
return s
|
return s
|
||||||
@ -283,7 +283,7 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po
|
|||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
# Populate the FormWrapper.
|
# Populate the FormWrapper.
|
||||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||||
|
|
||||||
c = template.RequestContext(request, {
|
c = template.RequestContext(request, {
|
||||||
'title': _('Add %s') % opts.verbose_name,
|
'title': _('Add %s') % opts.verbose_name,
|
||||||
@ -374,7 +374,7 @@ def change_stage(request, app_label, model_name, object_id):
|
|||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
# Populate the FormWrapper.
|
# Populate the FormWrapper.
|
||||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||||
form.original = manipulator.original_object
|
form.original = manipulator.original_object
|
||||||
form.order_objects = []
|
form.order_objects = []
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django import template, forms
|
from django import template, oldforms
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
@ -25,17 +25,17 @@ def template_validator(request):
|
|||||||
request.user.message_set.create(message='The template is valid.')
|
request.user.message_set.create(message='The template is valid.')
|
||||||
return render_to_response('admin/template_validator.html', {
|
return render_to_response('admin/template_validator.html', {
|
||||||
'title': 'Template validator',
|
'title': 'Template validator',
|
||||||
'form': forms.FormWrapper(manipulator, new_data, errors),
|
'form': oldforms.FormWrapper(manipulator, new_data, errors),
|
||||||
}, context_instance=template.RequestContext(request))
|
}, context_instance=template.RequestContext(request))
|
||||||
template_validator = staff_member_required(template_validator)
|
template_validator = staff_member_required(template_validator)
|
||||||
|
|
||||||
class TemplateValidator(forms.Manipulator):
|
class TemplateValidator(oldforms.Manipulator):
|
||||||
def __init__(self, settings_modules):
|
def __init__(self, settings_modules):
|
||||||
self.settings_modules = settings_modules
|
self.settings_modules = settings_modules
|
||||||
site_list = Site.objects.in_bulk(settings_modules.keys()).values()
|
site_list = Site.objects.in_bulk(settings_modules.keys()).values()
|
||||||
self.fields = (
|
self.fields = (
|
||||||
forms.SelectField('site', is_required=True, choices=[(s.id, s.name) for s in site_list]),
|
oldforms.SelectField('site', is_required=True, choices=[(s.id, s.name) for s in site_list]),
|
||||||
forms.LargeTextField('template', is_required=True, rows=25, validator_list=[self.isValidTemplate]),
|
oldforms.LargeTextField('template', is_required=True, rows=25, validator_list=[self.isValidTemplate]),
|
||||||
)
|
)
|
||||||
|
|
||||||
def isValidTemplate(self, field_data, all_data):
|
def isValidTemplate(self, field_data, all_data):
|
||||||
|
@ -3,16 +3,16 @@ from django.contrib.auth import authenticate
|
|||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.template import Context, loader
|
from django.template import Context, loader
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django import forms
|
from django import oldforms
|
||||||
|
|
||||||
class UserCreationForm(forms.Manipulator):
|
class UserCreationForm(oldforms.Manipulator):
|
||||||
"A form that creates a user, with no privileges, from the given username and password."
|
"A form that creates a user, with no privileges, from the given username and password."
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.fields = (
|
self.fields = (
|
||||||
forms.TextField(field_name='username', length=30, maxlength=30, is_required=True,
|
oldforms.TextField(field_name='username', length=30, maxlength=30, is_required=True,
|
||||||
validator_list=[validators.isAlphaNumeric, self.isValidUsername]),
|
validator_list=[validators.isAlphaNumeric, self.isValidUsername]),
|
||||||
forms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
|
oldforms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
|
||||||
forms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
|
oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
|
||||||
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
|
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class UserCreationForm(forms.Manipulator):
|
|||||||
"Creates the user."
|
"Creates the user."
|
||||||
return User.objects.create_user(new_data['username'], '', new_data['password1'])
|
return User.objects.create_user(new_data['username'], '', new_data['password1'])
|
||||||
|
|
||||||
class AuthenticationForm(forms.Manipulator):
|
class AuthenticationForm(oldforms.Manipulator):
|
||||||
"""
|
"""
|
||||||
Base class for authenticating users. Extend this to get a form that accepts
|
Base class for authenticating users. Extend this to get a form that accepts
|
||||||
username/password logins.
|
username/password logins.
|
||||||
@ -41,9 +41,9 @@ class AuthenticationForm(forms.Manipulator):
|
|||||||
"""
|
"""
|
||||||
self.request = request
|
self.request = request
|
||||||
self.fields = [
|
self.fields = [
|
||||||
forms.TextField(field_name="username", length=15, maxlength=30, is_required=True,
|
oldforms.TextField(field_name="username", length=15, maxlength=30, is_required=True,
|
||||||
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
|
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
|
||||||
forms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True),
|
oldforms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True),
|
||||||
]
|
]
|
||||||
self.user_cache = None
|
self.user_cache = None
|
||||||
|
|
||||||
@ -68,11 +68,11 @@ class AuthenticationForm(forms.Manipulator):
|
|||||||
def get_user(self):
|
def get_user(self):
|
||||||
return self.user_cache
|
return self.user_cache
|
||||||
|
|
||||||
class PasswordResetForm(forms.Manipulator):
|
class PasswordResetForm(oldforms.Manipulator):
|
||||||
"A form that lets a user request a password reset"
|
"A form that lets a user request a password reset"
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.fields = (
|
self.fields = (
|
||||||
forms.EmailField(field_name="email", length=40, is_required=True,
|
oldforms.EmailField(field_name="email", length=40, is_required=True,
|
||||||
validator_list=[self.isValidUserEmail]),
|
validator_list=[self.isValidUserEmail]),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -105,16 +105,16 @@ class PasswordResetForm(forms.Manipulator):
|
|||||||
}
|
}
|
||||||
send_mail('Password reset on %s' % site_name, t.render(Context(c)), None, [self.user_cache.email])
|
send_mail('Password reset on %s' % site_name, t.render(Context(c)), None, [self.user_cache.email])
|
||||||
|
|
||||||
class PasswordChangeForm(forms.Manipulator):
|
class PasswordChangeForm(oldforms.Manipulator):
|
||||||
"A form that lets a user change his password."
|
"A form that lets a user change his password."
|
||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.fields = (
|
self.fields = (
|
||||||
forms.PasswordField(field_name="old_password", length=30, maxlength=30, is_required=True,
|
oldforms.PasswordField(field_name="old_password", length=30, maxlength=30, is_required=True,
|
||||||
validator_list=[self.isValidOldPassword]),
|
validator_list=[self.isValidOldPassword]),
|
||||||
forms.PasswordField(field_name="new_password1", length=30, maxlength=30, is_required=True,
|
oldforms.PasswordField(field_name="new_password1", length=30, maxlength=30, is_required=True,
|
||||||
validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]),
|
validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]),
|
||||||
forms.PasswordField(field_name="new_password2", length=30, maxlength=30, is_required=True),
|
oldforms.PasswordField(field_name="new_password2", length=30, maxlength=30, is_required=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
def isValidOldPassword(self, new_data, all_data):
|
def isValidOldPassword(self, new_data, all_data):
|
||||||
@ -126,3 +126,18 @@ class PasswordChangeForm(forms.Manipulator):
|
|||||||
"Saves the new password."
|
"Saves the new password."
|
||||||
self.user.set_password(new_data['new_password1'])
|
self.user.set_password(new_data['new_password1'])
|
||||||
self.user.save()
|
self.user.save()
|
||||||
|
|
||||||
|
class AdminPasswordChangeForm(oldforms.Manipulator):
|
||||||
|
"A form used to change the password of a user in the admin interface."
|
||||||
|
def __init__(self, user):
|
||||||
|
self.user = user
|
||||||
|
self.fields = (
|
||||||
|
oldforms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
|
||||||
|
oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
|
||||||
|
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
|
||||||
|
)
|
||||||
|
|
||||||
|
def save(self, new_data):
|
||||||
|
"Saves the new password."
|
||||||
|
self.user.set_password(new_data['password1'])
|
||||||
|
self.user.save()
|
||||||
|
@ -91,7 +91,7 @@ class User(models.Model):
|
|||||||
first_name = models.CharField(_('first name'), maxlength=30, blank=True)
|
first_name = models.CharField(_('first name'), maxlength=30, blank=True)
|
||||||
last_name = models.CharField(_('last name'), maxlength=30, blank=True)
|
last_name = models.CharField(_('last name'), maxlength=30, blank=True)
|
||||||
email = models.EmailField(_('e-mail address'), blank=True)
|
email = models.EmailField(_('e-mail address'), blank=True)
|
||||||
password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]'"))
|
password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
|
||||||
is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
|
is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
|
||||||
is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user can log into the Django admin. Unselect this instead of deleting accounts."))
|
is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user can log into the Django admin. Unselect this instead of deleting accounts."))
|
||||||
is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
|
is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.contrib.auth.forms import AuthenticationForm
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm
|
from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm
|
||||||
from django import forms
|
from django import oldforms
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
@ -26,7 +26,7 @@ def login(request, template_name='registration/login.html'):
|
|||||||
errors = {}
|
errors = {}
|
||||||
request.session.set_test_cookie()
|
request.session.set_test_cookie()
|
||||||
return render_to_response(template_name, {
|
return render_to_response(template_name, {
|
||||||
'form': forms.FormWrapper(manipulator, request.POST, errors),
|
'form': oldforms.FormWrapper(manipulator, request.POST, errors),
|
||||||
REDIRECT_FIELD_NAME: redirect_to,
|
REDIRECT_FIELD_NAME: redirect_to,
|
||||||
'site_name': Site.objects.get_current().name,
|
'site_name': Site.objects.get_current().name,
|
||||||
}, context_instance=RequestContext(request))
|
}, context_instance=RequestContext(request))
|
||||||
@ -62,7 +62,7 @@ def password_reset(request, is_admin_site=False, template_name='registration/pas
|
|||||||
else:
|
else:
|
||||||
form.save(email_template_name=email_template_name)
|
form.save(email_template_name=email_template_name)
|
||||||
return HttpResponseRedirect('%sdone/' % request.path)
|
return HttpResponseRedirect('%sdone/' % request.path)
|
||||||
return render_to_response(template_name, {'form': forms.FormWrapper(form, new_data, errors)},
|
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
|
||||||
context_instance=RequestContext(request))
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
def password_reset_done(request, template_name='registration/password_reset_done.html'):
|
def password_reset_done(request, template_name='registration/password_reset_done.html'):
|
||||||
@ -77,7 +77,7 @@ def password_change(request, template_name='registration/password_change_form.ht
|
|||||||
if not errors:
|
if not errors:
|
||||||
form.save(new_data)
|
form.save(new_data)
|
||||||
return HttpResponseRedirect('%sdone/' % request.path)
|
return HttpResponseRedirect('%sdone/' % request.path)
|
||||||
return render_to_response(template_name, {'form': forms.FormWrapper(form, new_data, errors)},
|
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
|
||||||
context_instance=RequestContext(request))
|
context_instance=RequestContext(request))
|
||||||
password_change = login_required(password_change)
|
password_change = login_required(password_change)
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django import forms
|
from django import oldforms
|
||||||
from django.core.mail import mail_admins, mail_managers
|
from django.core.mail import mail_admins, mail_managers
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
@ -28,37 +28,37 @@ class PublicCommentManipulator(AuthenticationForm):
|
|||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
self.fields.extend([
|
self.fields.extend([
|
||||||
forms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
oldforms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
||||||
validator_list=[self.hasNoProfanities]),
|
validator_list=[self.hasNoProfanities]),
|
||||||
forms.RadioSelectField(field_name="rating1", choices=choices,
|
oldforms.RadioSelectField(field_name="rating1", choices=choices,
|
||||||
is_required=ratings_required and num_rating_choices > 0,
|
is_required=ratings_required and num_rating_choices > 0,
|
||||||
validator_list=get_validator_list(1),
|
validator_list=get_validator_list(1),
|
||||||
),
|
),
|
||||||
forms.RadioSelectField(field_name="rating2", choices=choices,
|
oldforms.RadioSelectField(field_name="rating2", choices=choices,
|
||||||
is_required=ratings_required and num_rating_choices > 1,
|
is_required=ratings_required and num_rating_choices > 1,
|
||||||
validator_list=get_validator_list(2),
|
validator_list=get_validator_list(2),
|
||||||
),
|
),
|
||||||
forms.RadioSelectField(field_name="rating3", choices=choices,
|
oldforms.RadioSelectField(field_name="rating3", choices=choices,
|
||||||
is_required=ratings_required and num_rating_choices > 2,
|
is_required=ratings_required and num_rating_choices > 2,
|
||||||
validator_list=get_validator_list(3),
|
validator_list=get_validator_list(3),
|
||||||
),
|
),
|
||||||
forms.RadioSelectField(field_name="rating4", choices=choices,
|
oldforms.RadioSelectField(field_name="rating4", choices=choices,
|
||||||
is_required=ratings_required and num_rating_choices > 3,
|
is_required=ratings_required and num_rating_choices > 3,
|
||||||
validator_list=get_validator_list(4),
|
validator_list=get_validator_list(4),
|
||||||
),
|
),
|
||||||
forms.RadioSelectField(field_name="rating5", choices=choices,
|
oldforms.RadioSelectField(field_name="rating5", choices=choices,
|
||||||
is_required=ratings_required and num_rating_choices > 4,
|
is_required=ratings_required and num_rating_choices > 4,
|
||||||
validator_list=get_validator_list(5),
|
validator_list=get_validator_list(5),
|
||||||
),
|
),
|
||||||
forms.RadioSelectField(field_name="rating6", choices=choices,
|
oldforms.RadioSelectField(field_name="rating6", choices=choices,
|
||||||
is_required=ratings_required and num_rating_choices > 5,
|
is_required=ratings_required and num_rating_choices > 5,
|
||||||
validator_list=get_validator_list(6),
|
validator_list=get_validator_list(6),
|
||||||
),
|
),
|
||||||
forms.RadioSelectField(field_name="rating7", choices=choices,
|
oldforms.RadioSelectField(field_name="rating7", choices=choices,
|
||||||
is_required=ratings_required and num_rating_choices > 6,
|
is_required=ratings_required and num_rating_choices > 6,
|
||||||
validator_list=get_validator_list(7),
|
validator_list=get_validator_list(7),
|
||||||
),
|
),
|
||||||
forms.RadioSelectField(field_name="rating8", choices=choices,
|
oldforms.RadioSelectField(field_name="rating8", choices=choices,
|
||||||
is_required=ratings_required and num_rating_choices > 7,
|
is_required=ratings_required and num_rating_choices > 7,
|
||||||
validator_list=get_validator_list(8),
|
validator_list=get_validator_list(8),
|
||||||
),
|
),
|
||||||
@ -117,13 +117,13 @@ class PublicCommentManipulator(AuthenticationForm):
|
|||||||
mail_managers("Comment posted by sketchy user (%s)" % self.user_cache.username, c.get_as_text())
|
mail_managers("Comment posted by sketchy user (%s)" % self.user_cache.username, c.get_as_text())
|
||||||
return c
|
return c
|
||||||
|
|
||||||
class PublicFreeCommentManipulator(forms.Manipulator):
|
class PublicFreeCommentManipulator(oldforms.Manipulator):
|
||||||
"Manipulator that handles public free (unregistered) comments"
|
"Manipulator that handles public free (unregistered) comments"
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.fields = (
|
self.fields = (
|
||||||
forms.TextField(field_name="person_name", maxlength=50, is_required=True,
|
oldforms.TextField(field_name="person_name", maxlength=50, is_required=True,
|
||||||
validator_list=[self.hasNoProfanities]),
|
validator_list=[self.hasNoProfanities]),
|
||||||
forms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
oldforms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
||||||
validator_list=[self.hasNoProfanities]),
|
validator_list=[self.hasNoProfanities]),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -221,9 +221,9 @@ def post_comment(request):
|
|||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
login(request, manipulator.get_user())
|
login(request, manipulator.get_user())
|
||||||
if errors or request.POST.has_key('preview'):
|
if errors or request.POST.has_key('preview'):
|
||||||
class CommentFormWrapper(forms.FormWrapper):
|
class CommentFormWrapper(oldforms.FormWrapper):
|
||||||
def __init__(self, manipulator, new_data, errors, rating_choices):
|
def __init__(self, manipulator, new_data, errors, rating_choices):
|
||||||
forms.FormWrapper.__init__(self, manipulator, new_data, errors)
|
oldforms.FormWrapper.__init__(self, manipulator, new_data, errors)
|
||||||
self.rating_choices = rating_choices
|
self.rating_choices = rating_choices
|
||||||
def ratings(self):
|
def ratings(self):
|
||||||
field_list = [self['rating%d' % (i+1)] for i in range(len(rating_choices))]
|
field_list = [self['rating%d' % (i+1)] for i in range(len(rating_choices))]
|
||||||
@ -302,7 +302,7 @@ def post_free_comment(request):
|
|||||||
comment = errors and '' or manipulator.get_comment(new_data)
|
comment = errors and '' or manipulator.get_comment(new_data)
|
||||||
return render_to_response('comments/free_preview.html', {
|
return render_to_response('comments/free_preview.html', {
|
||||||
'comment': comment,
|
'comment': comment,
|
||||||
'comment_form': forms.FormWrapper(manipulator, new_data, errors),
|
'comment_form': oldforms.FormWrapper(manipulator, new_data, errors),
|
||||||
'options': options,
|
'options': options,
|
||||||
'target': target,
|
'target': target,
|
||||||
'hash': security_hash,
|
'hash': security_hash,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
CONTENT_TYPE_CACHE = {}
|
||||||
class ContentTypeManager(models.Manager):
|
class ContentTypeManager(models.Manager):
|
||||||
def get_for_model(self, model):
|
def get_for_model(self, model):
|
||||||
"""
|
"""
|
||||||
@ -8,10 +9,15 @@ class ContentTypeManager(models.Manager):
|
|||||||
ContentType if necessary.
|
ContentType if necessary.
|
||||||
"""
|
"""
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
|
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
|
# The str() is needed around opts.verbose_name because it's a
|
||||||
# django.utils.functional.__proxy__ object.
|
# django.utils.functional.__proxy__ object.
|
||||||
ct, created = self.model._default_manager.get_or_create(app_label=opts.app_label,
|
ct, created = self.model._default_manager.get_or_create(app_label=key[0],
|
||||||
model=opts.object_name.lower(), defaults={'name': str(opts.verbose_name)})
|
model=key[1], defaults={'name': str(opts.verbose_name)})
|
||||||
|
CONTENT_TYPE_CACHE[key] = ct
|
||||||
return ct
|
return ct
|
||||||
|
|
||||||
class ContentType(models.Model):
|
class ContentType(models.Model):
|
||||||
|
@ -11,7 +11,7 @@ import md5
|
|||||||
import re
|
import re
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
_ERROR_MSG = "<h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p>"
|
_ERROR_MSG = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>'
|
||||||
|
|
||||||
_POST_FORM_RE = \
|
_POST_FORM_RE = \
|
||||||
re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
|
re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
|
||||||
|
@ -48,6 +48,7 @@ from django.conf import settings
|
|||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
|
from django.template.context import RequestContext
|
||||||
import cPickle as pickle
|
import cPickle as pickle
|
||||||
import md5
|
import md5
|
||||||
|
|
||||||
@ -91,7 +92,9 @@ class FormPreview(object):
|
|||||||
def preview_get(self, request):
|
def preview_get(self, request):
|
||||||
"Displays the form"
|
"Displays the form"
|
||||||
f = self.form(auto_id=AUTO_ID)
|
f = self.form(auto_id=AUTO_ID)
|
||||||
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state})
|
return render_to_response(self.form_template,
|
||||||
|
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
|
||||||
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
def preview_post(self, request):
|
def preview_post(self, request):
|
||||||
"Validates the POST data. If valid, displays the preview page. Else, redisplays form."
|
"Validates the POST data. If valid, displays the preview page. Else, redisplays form."
|
||||||
@ -100,9 +103,9 @@ class FormPreview(object):
|
|||||||
if f.is_valid():
|
if f.is_valid():
|
||||||
context['hash_field'] = self.unused_name('hash')
|
context['hash_field'] = self.unused_name('hash')
|
||||||
context['hash_value'] = self.security_hash(request, f)
|
context['hash_value'] = self.security_hash(request, f)
|
||||||
return render_to_response(self.preview_template, context)
|
return render_to_response(self.preview_template, context, context_instance=RequestContext(request))
|
||||||
else:
|
else:
|
||||||
return render_to_response(self.form_template, context)
|
return render_to_response(self.form_template, context, context_instance=RequestContext(request))
|
||||||
|
|
||||||
def post_post(self, request):
|
def post_post(self, request):
|
||||||
"Validates the POST data. If valid, calls done(). Else, redisplays form."
|
"Validates the POST data. If valid, calls done(). Else, redisplays form."
|
||||||
@ -112,7 +115,9 @@ class FormPreview(object):
|
|||||||
return self.failed_hash(request) # Security hash failed.
|
return self.failed_hash(request) # Security hash failed.
|
||||||
return self.done(request, f.clean_data)
|
return self.done(request, f.clean_data)
|
||||||
else:
|
else:
|
||||||
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state})
|
return render_to_response(self.form_template,
|
||||||
|
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
|
||||||
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
|
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sessions.models import Session
|
from django.contrib.sessions.models import Session
|
||||||
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ class SessionWrapper(object):
|
|||||||
s = Session.objects.get(session_key=self.session_key,
|
s = Session.objects.get(session_key=self.session_key,
|
||||||
expire_date__gt=datetime.datetime.now())
|
expire_date__gt=datetime.datetime.now())
|
||||||
self._session_cache = s.get_decoded()
|
self._session_cache = s.get_decoded()
|
||||||
except Session.DoesNotExist:
|
except (Session.DoesNotExist, SuspiciousOperation):
|
||||||
self._session_cache = {}
|
self._session_cache = {}
|
||||||
# Set the session_key to None to force creation of a new
|
# Set the session_key to None to force creation of a new
|
||||||
# key, for extra security.
|
# key, for extra security.
|
||||||
|
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):
|
def __init__(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, key, default=None):
|
||||||
pass
|
return default
|
||||||
|
|
||||||
def set(self, *args, **kwargs):
|
def set(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
@ -16,7 +16,7 @@ class CacheClass(BaseCache):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def get_many(self, *args, **kwargs):
|
def get_many(self, *args, **kwargs):
|
||||||
pass
|
return {}
|
||||||
|
|
||||||
def has_key(self, *args, **kwargs):
|
def has_key(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
@ -60,7 +60,10 @@ class BaseHandler(object):
|
|||||||
if response:
|
if response:
|
||||||
return response
|
return response
|
||||||
|
|
||||||
resolver = urlresolvers.RegexURLResolver(r'^/', settings.ROOT_URLCONF)
|
# Get urlconf from request object, if available. Otherwise use default.
|
||||||
|
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
|
||||||
|
|
||||||
|
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
|
||||||
try:
|
try:
|
||||||
callback, callback_args, callback_kwargs = resolver.resolve(request.path)
|
callback, callback_args, callback_kwargs = resolver.resolve(request.path)
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ APP_ARGS = '[appname ...]'
|
|||||||
# which has been installed.
|
# which has been installed.
|
||||||
PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
|
PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
|
||||||
|
|
||||||
INVALID_PROJECT_NAMES = ('django', 'test')
|
INVALID_PROJECT_NAMES = ('django', 'site', 'test')
|
||||||
|
|
||||||
# Set up the terminal color scheme.
|
# Set up the terminal color scheme.
|
||||||
class dummy: pass
|
class dummy: pass
|
||||||
@ -708,7 +708,7 @@ def startproject(project_name, directory):
|
|||||||
"Creates a Django project for the given project_name in the given directory."
|
"Creates a Django project for the given project_name in the given directory."
|
||||||
from random import choice
|
from random import choice
|
||||||
if project_name in INVALID_PROJECT_NAMES:
|
if project_name in INVALID_PROJECT_NAMES:
|
||||||
sys.stderr.write(style.ERROR("Error: %r isn't a valid project name. Please try another.\n" % project_name))
|
sys.stderr.write(style.ERROR("Error: '%r' conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name.\n" % project_name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
_start_helper('project', project_name, directory)
|
_start_helper('project', project_name, directory)
|
||||||
# Create a random SECRET_KEY hash, and put it in the main settings.
|
# Create a random SECRET_KEY hash, and put it in the main settings.
|
||||||
|
@ -57,7 +57,7 @@ def Deserializer(object_list, **options):
|
|||||||
for d in object_list:
|
for d in object_list:
|
||||||
# Look up the model and starting build a dict of data for it.
|
# Look up the model and starting build a dict of data for it.
|
||||||
Model = _get_model(d["model"])
|
Model = _get_model(d["model"])
|
||||||
data = {Model._meta.pk.name : d["pk"]}
|
data = {Model._meta.pk.attname : d["pk"]}
|
||||||
m2m_data = {}
|
m2m_data = {}
|
||||||
|
|
||||||
# Handle each field
|
# Handle each field
|
||||||
|
@ -21,6 +21,5 @@ DATA_TYPES = {
|
|||||||
'SmallIntegerField': 'smallint',
|
'SmallIntegerField': 'smallint',
|
||||||
'TextField': 'text',
|
'TextField': 'text',
|
||||||
'TimeField': 'time',
|
'TimeField': 'time',
|
||||||
'URLField': 'varchar(200)',
|
|
||||||
'USStateField': 'varchar(2)',
|
'USStateField': 'varchar(2)',
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,8 @@ class DatabaseWrapper(local):
|
|||||||
cursor = self.connection.cursor()
|
cursor = self.connection.cursor()
|
||||||
if self.connection.get_server_info() >= '4.1':
|
if self.connection.get_server_info() >= '4.1':
|
||||||
cursor.execute("SET NAMES 'utf8'")
|
cursor.execute("SET NAMES 'utf8'")
|
||||||
|
else:
|
||||||
|
cursor = self.connection.cursor()
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
|
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
|
||||||
return cursor
|
return cursor
|
||||||
|
@ -25,6 +25,5 @@ DATA_TYPES = {
|
|||||||
'SmallIntegerField': 'smallint',
|
'SmallIntegerField': 'smallint',
|
||||||
'TextField': 'longtext',
|
'TextField': 'longtext',
|
||||||
'TimeField': 'time',
|
'TimeField': 'time',
|
||||||
'URLField': 'varchar(200)',
|
|
||||||
'USStateField': 'varchar(2)',
|
'USStateField': 'varchar(2)',
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,5 @@ DATA_TYPES = {
|
|||||||
'SmallIntegerField': 'smallint',
|
'SmallIntegerField': 'smallint',
|
||||||
'TextField': 'long',
|
'TextField': 'long',
|
||||||
'TimeField': 'timestamp',
|
'TimeField': 'timestamp',
|
||||||
'URLField': 'varchar(200)',
|
|
||||||
'USStateField': 'varchar(2)',
|
'USStateField': 'varchar(2)',
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,38 @@ except ImportError:
|
|||||||
# Import copy of _thread_local.py from Python 2.4
|
# Import copy of _thread_local.py from Python 2.4
|
||||||
from django.utils._threading_local import local
|
from django.utils._threading_local import local
|
||||||
|
|
||||||
|
def smart_basestring(s, charset):
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
return s.encode(charset)
|
||||||
|
return s
|
||||||
|
|
||||||
|
class UnicodeCursorWrapper(object):
|
||||||
|
"""
|
||||||
|
A thin wrapper around psycopg cursors that allows them to accept Unicode
|
||||||
|
strings as params.
|
||||||
|
|
||||||
|
This is necessary because psycopg doesn't apply any DB quoting to
|
||||||
|
parameters that are Unicode strings. If a param is Unicode, this will
|
||||||
|
convert it to a bytestring using DEFAULT_CHARSET before passing it to
|
||||||
|
psycopg.
|
||||||
|
"""
|
||||||
|
def __init__(self, cursor, charset):
|
||||||
|
self.cursor = cursor
|
||||||
|
self.charset = charset
|
||||||
|
|
||||||
|
def execute(self, sql, params=()):
|
||||||
|
return self.cursor.execute(sql, [smart_basestring(p, self.charset) for p in params])
|
||||||
|
|
||||||
|
def executemany(self, sql, param_list):
|
||||||
|
new_param_list = [tuple([smart_basestring(p, self.charset) for p in params]) for params in param_list]
|
||||||
|
return self.cursor.executemany(sql, new_param_list)
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
if self.__dict__.has_key(attr):
|
||||||
|
return self.__dict__[attr]
|
||||||
|
else:
|
||||||
|
return getattr(self.cursor, attr)
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseWrapper(local):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.connection = None
|
self.connection = None
|
||||||
@ -45,6 +77,7 @@ class DatabaseWrapper(local):
|
|||||||
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
|
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
|
||||||
cursor = self.connection.cursor()
|
cursor = self.connection.cursor()
|
||||||
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
||||||
|
cursor = UnicodeCursorWrapper(cursor, settings.DEFAULT_CHARSET)
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
return util.CursorDebugWrapper(cursor, self)
|
return util.CursorDebugWrapper(cursor, self)
|
||||||
return cursor
|
return cursor
|
||||||
@ -118,7 +151,7 @@ def get_pk_default_value():
|
|||||||
try:
|
try:
|
||||||
Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
|
Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise Exception, "You appear to be using psycopg version 2, which isn't supported yet, because it's still in beta. Use psycopg version 1 instead: http://initd.org/projects/psycopg1"
|
raise Exception, "You appear to be using psycopg version 2. Set your DATABASE_ENGINE to 'postgresql_psycopg2' instead of 'postgresql'."
|
||||||
Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time))
|
Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time))
|
||||||
Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp))
|
Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp))
|
||||||
Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean))
|
Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean))
|
||||||
|
@ -25,6 +25,5 @@ DATA_TYPES = {
|
|||||||
'SmallIntegerField': 'smallint',
|
'SmallIntegerField': 'smallint',
|
||||||
'TextField': 'text',
|
'TextField': 'text',
|
||||||
'TimeField': 'time',
|
'TimeField': 'time',
|
||||||
'URLField': 'varchar(200)',
|
|
||||||
'USStateField': 'varchar(2)',
|
'USStateField': 'varchar(2)',
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,5 @@ DATA_TYPES = {
|
|||||||
'SmallIntegerField': 'smallint',
|
'SmallIntegerField': 'smallint',
|
||||||
'TextField': 'text',
|
'TextField': 'text',
|
||||||
'TimeField': 'time',
|
'TimeField': 'time',
|
||||||
'URLField': 'varchar(200)',
|
|
||||||
'USStateField': 'varchar(2)',
|
'USStateField': 'varchar(2)',
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ from django.db.models import signals
|
|||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django import forms
|
from django import oldforms
|
||||||
|
from django import newforms as forms
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.utils.functional import curry
|
from django.utils.functional import curry
|
||||||
from django.utils.itercompat import tee
|
from django.utils.itercompat import tee
|
||||||
@ -206,10 +207,10 @@ class Field(object):
|
|||||||
|
|
||||||
if self.choices:
|
if self.choices:
|
||||||
if self.radio_admin:
|
if self.radio_admin:
|
||||||
field_objs = [forms.RadioSelectField]
|
field_objs = [oldforms.RadioSelectField]
|
||||||
params['ul_class'] = get_ul_class(self.radio_admin)
|
params['ul_class'] = get_ul_class(self.radio_admin)
|
||||||
else:
|
else:
|
||||||
field_objs = [forms.SelectField]
|
field_objs = [oldforms.SelectField]
|
||||||
|
|
||||||
params['choices'] = self.get_choices_default()
|
params['choices'] = self.get_choices_default()
|
||||||
else:
|
else:
|
||||||
@ -218,7 +219,7 @@ class Field(object):
|
|||||||
|
|
||||||
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
|
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
|
||||||
"""
|
"""
|
||||||
Returns a list of forms.FormField instances for this field. It
|
Returns a list of oldforms.FormField instances for this field. It
|
||||||
calculates the choices at runtime, not at compile time.
|
calculates the choices at runtime, not at compile time.
|
||||||
|
|
||||||
name_prefix is a prefix to prepend to the "field_name" argument.
|
name_prefix is a prefix to prepend to the "field_name" argument.
|
||||||
@ -333,6 +334,16 @@ class Field(object):
|
|||||||
return self._choices
|
return self._choices
|
||||||
choices = property(_get_choices)
|
choices = property(_get_choices)
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
"Returns a django.newforms.Field instance for this database Field."
|
||||||
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.CharField(**defaults)
|
||||||
|
|
||||||
|
def value_from_object(self, obj):
|
||||||
|
"Returns the value of this field in the given model instance."
|
||||||
|
return getattr(obj, self.attname)
|
||||||
|
|
||||||
class AutoField(Field):
|
class AutoField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -354,7 +365,7 @@ class AutoField(Field):
|
|||||||
return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
|
return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.HiddenField]
|
return [oldforms.HiddenField]
|
||||||
|
|
||||||
def get_manipulator_new_data(self, new_data, rel=False):
|
def get_manipulator_new_data(self, new_data, rel=False):
|
||||||
# Never going to be called
|
# Never going to be called
|
||||||
@ -369,6 +380,9 @@ class AutoField(Field):
|
|||||||
super(AutoField, self).contribute_to_class(cls, name)
|
super(AutoField, self).contribute_to_class(cls, name)
|
||||||
cls._meta.has_auto_field = True
|
cls._meta.has_auto_field = True
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
return None
|
||||||
|
|
||||||
class BooleanField(Field):
|
class BooleanField(Field):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
kwargs['blank'] = True
|
kwargs['blank'] = True
|
||||||
@ -381,11 +395,16 @@ class BooleanField(Field):
|
|||||||
raise validators.ValidationError, gettext("This value must be either True or False.")
|
raise validators.ValidationError, gettext("This value must be either True or False.")
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.CheckboxField]
|
return [oldforms.CheckboxField]
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.BooleanField(**defaults)
|
||||||
|
|
||||||
class CharField(Field):
|
class CharField(Field):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.TextField]
|
return [oldforms.TextField]
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if isinstance(value, basestring):
|
if isinstance(value, basestring):
|
||||||
@ -397,10 +416,15 @@ class CharField(Field):
|
|||||||
raise validators.ValidationError, gettext_lazy("This field cannot be null.")
|
raise validators.ValidationError, gettext_lazy("This field cannot be null.")
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'max_length': self.maxlength, 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.CharField(**defaults)
|
||||||
|
|
||||||
# TODO: Maybe move this into contrib, because it's specialized.
|
# TODO: Maybe move this into contrib, because it's specialized.
|
||||||
class CommaSeparatedIntegerField(CharField):
|
class CommaSeparatedIntegerField(CharField):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.CommaSeparatedIntegerField]
|
return [oldforms.CommaSeparatedIntegerField]
|
||||||
|
|
||||||
class DateField(Field):
|
class DateField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
@ -462,12 +486,17 @@ class DateField(Field):
|
|||||||
return Field.get_db_prep_save(self, value)
|
return Field.get_db_prep_save(self, value)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.DateField]
|
return [oldforms.DateField]
|
||||||
|
|
||||||
def flatten_data(self, follow, obj=None):
|
def flatten_data(self, follow, obj=None):
|
||||||
val = self._get_val_from_obj(obj)
|
val = self._get_val_from_obj(obj)
|
||||||
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
|
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.DateField(**defaults)
|
||||||
|
|
||||||
class DateTimeField(DateField):
|
class DateTimeField(DateField):
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
@ -503,7 +532,7 @@ class DateTimeField(DateField):
|
|||||||
return Field.get_db_prep_lookup(self, lookup_type, value)
|
return Field.get_db_prep_lookup(self, lookup_type, value)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.DateField, forms.TimeField]
|
return [oldforms.DateField, oldforms.TimeField]
|
||||||
|
|
||||||
def get_manipulator_field_names(self, name_prefix):
|
def get_manipulator_field_names(self, name_prefix):
|
||||||
return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
|
return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
|
||||||
@ -526,6 +555,11 @@ class DateTimeField(DateField):
|
|||||||
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
|
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
|
||||||
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
|
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.DateTimeField(**defaults)
|
||||||
|
|
||||||
class EmailField(CharField):
|
class EmailField(CharField):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
kwargs['maxlength'] = 75
|
kwargs['maxlength'] = 75
|
||||||
@ -535,11 +569,16 @@ class EmailField(CharField):
|
|||||||
return "CharField"
|
return "CharField"
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.EmailField]
|
return [oldforms.EmailField]
|
||||||
|
|
||||||
def validate(self, field_data, all_data):
|
def validate(self, field_data, all_data):
|
||||||
validators.isValidEmail(field_data, all_data)
|
validators.isValidEmail(field_data, all_data)
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.EmailField(**defaults)
|
||||||
|
|
||||||
class FileField(Field):
|
class FileField(Field):
|
||||||
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
|
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
|
||||||
self.upload_to = upload_to
|
self.upload_to = upload_to
|
||||||
@ -599,7 +638,7 @@ class FileField(Field):
|
|||||||
os.remove(file_name)
|
os.remove(file_name)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.FileUploadField, forms.HiddenField]
|
return [oldforms.FileUploadField, oldforms.HiddenField]
|
||||||
|
|
||||||
def get_manipulator_field_names(self, name_prefix):
|
def get_manipulator_field_names(self, name_prefix):
|
||||||
return [name_prefix + self.name + '_file', name_prefix + self.name]
|
return [name_prefix + self.name + '_file', name_prefix + self.name]
|
||||||
@ -627,7 +666,7 @@ class FilePathField(Field):
|
|||||||
Field.__init__(self, verbose_name, name, **kwargs)
|
Field.__init__(self, verbose_name, name, **kwargs)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [curry(forms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
|
return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
|
||||||
|
|
||||||
class FloatField(Field):
|
class FloatField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
@ -636,7 +675,7 @@ class FloatField(Field):
|
|||||||
Field.__init__(self, verbose_name, name, **kwargs)
|
Field.__init__(self, verbose_name, name, **kwargs)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [curry(forms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
|
return [curry(oldforms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
|
||||||
|
|
||||||
class ImageField(FileField):
|
class ImageField(FileField):
|
||||||
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
|
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
|
||||||
@ -644,7 +683,7 @@ class ImageField(FileField):
|
|||||||
FileField.__init__(self, verbose_name, name, **kwargs)
|
FileField.__init__(self, verbose_name, name, **kwargs)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.ImageUploadField, forms.HiddenField]
|
return [oldforms.ImageUploadField, oldforms.HiddenField]
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
super(ImageField, self).contribute_to_class(cls, name)
|
super(ImageField, self).contribute_to_class(cls, name)
|
||||||
@ -670,7 +709,12 @@ class ImageField(FileField):
|
|||||||
class IntegerField(Field):
|
class IntegerField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.IntegerField]
|
return [oldforms.IntegerField]
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.IntegerField(**defaults)
|
||||||
|
|
||||||
class IPAddressField(Field):
|
class IPAddressField(Field):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -678,7 +722,7 @@ class IPAddressField(Field):
|
|||||||
Field.__init__(self, *args, **kwargs)
|
Field.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.IPAddressField]
|
return [oldforms.IPAddressField]
|
||||||
|
|
||||||
def validate(self, field_data, all_data):
|
def validate(self, field_data, all_data):
|
||||||
validators.isValidIPAddress4(field_data, None)
|
validators.isValidIPAddress4(field_data, None)
|
||||||
@ -689,22 +733,22 @@ class NullBooleanField(Field):
|
|||||||
Field.__init__(self, *args, **kwargs)
|
Field.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.NullBooleanField]
|
return [oldforms.NullBooleanField]
|
||||||
|
|
||||||
class PhoneNumberField(IntegerField):
|
class PhoneNumberField(IntegerField):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.PhoneNumberField]
|
return [oldforms.PhoneNumberField]
|
||||||
|
|
||||||
def validate(self, field_data, all_data):
|
def validate(self, field_data, all_data):
|
||||||
validators.isValidPhone(field_data, all_data)
|
validators.isValidPhone(field_data, all_data)
|
||||||
|
|
||||||
class PositiveIntegerField(IntegerField):
|
class PositiveIntegerField(IntegerField):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.PositiveIntegerField]
|
return [oldforms.PositiveIntegerField]
|
||||||
|
|
||||||
class PositiveSmallIntegerField(IntegerField):
|
class PositiveSmallIntegerField(IntegerField):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.PositiveSmallIntegerField]
|
return [oldforms.PositiveSmallIntegerField]
|
||||||
|
|
||||||
class SlugField(Field):
|
class SlugField(Field):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -716,15 +760,20 @@ class SlugField(Field):
|
|||||||
Field.__init__(self, *args, **kwargs)
|
Field.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.TextField]
|
return [oldforms.TextField]
|
||||||
|
|
||||||
class SmallIntegerField(IntegerField):
|
class SmallIntegerField(IntegerField):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.SmallIntegerField]
|
return [oldforms.SmallIntegerField]
|
||||||
|
|
||||||
class TextField(Field):
|
class TextField(Field):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.LargeTextField]
|
return [oldforms.LargeTextField]
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'widget': forms.Textarea, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.CharField(**defaults)
|
||||||
|
|
||||||
class TimeField(Field):
|
class TimeField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
@ -760,24 +809,39 @@ class TimeField(Field):
|
|||||||
return Field.get_db_prep_save(self, value)
|
return Field.get_db_prep_save(self, value)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.TimeField]
|
return [oldforms.TimeField]
|
||||||
|
|
||||||
def flatten_data(self,follow, obj = None):
|
def flatten_data(self,follow, obj = None):
|
||||||
val = self._get_val_from_obj(obj)
|
val = self._get_val_from_obj(obj)
|
||||||
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
|
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||||
|
|
||||||
class URLField(Field):
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.TimeField(**defaults)
|
||||||
|
|
||||||
|
class URLField(CharField):
|
||||||
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
|
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
|
||||||
|
kwargs['maxlength'] = kwargs.get('maxlength', 200)
|
||||||
if verify_exists:
|
if verify_exists:
|
||||||
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
|
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
|
||||||
Field.__init__(self, verbose_name, name, **kwargs)
|
self.verify_exists = verify_exists
|
||||||
|
CharField.__init__(self, verbose_name, name, **kwargs)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.URLField]
|
return [oldforms.URLField]
|
||||||
|
|
||||||
|
def get_internal_type(self):
|
||||||
|
return "CharField"
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'verify_exists': self.verify_exists, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.URLField(**defaults)
|
||||||
|
|
||||||
class USStateField(Field):
|
class USStateField(Field):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [forms.USStateField]
|
return [oldforms.USStateField]
|
||||||
|
|
||||||
class XMLField(TextField):
|
class XMLField(TextField):
|
||||||
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
|
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
|
||||||
@ -788,7 +852,7 @@ class XMLField(TextField):
|
|||||||
return "TextField"
|
return "TextField"
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [curry(forms.XMLLargeTextField, schema_path=self.schema_path)]
|
return [curry(oldforms.XMLLargeTextField, schema_path=self.schema_path)]
|
||||||
|
|
||||||
class OrderingField(IntegerField):
|
class OrderingField(IntegerField):
|
||||||
empty_strings_allowed=False
|
empty_strings_allowed=False
|
||||||
@ -801,4 +865,4 @@ class OrderingField(IntegerField):
|
|||||||
return "IntegerField"
|
return "IntegerField"
|
||||||
|
|
||||||
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
|
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
|
||||||
return [forms.HiddenField(name_prefix + self.name)]
|
return [oldforms.HiddenField(name_prefix + self.name)]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
Classes allowing "generic" relations through ContentType and object-id fields.
|
Classes allowing "generic" relations through ContentType and object-id fields.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django import forms
|
from django import oldforms
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import backend
|
from django.db import backend
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
@ -98,7 +98,7 @@ class GenericRelation(RelatedField, Field):
|
|||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
choices = self.get_choices_default()
|
choices = self.get_choices_default()
|
||||||
return [curry(forms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
||||||
|
|
||||||
def get_choices_default(self):
|
def get_choices_default(self):
|
||||||
return Field.get_choices(self, include_blank=False)
|
return Field.get_choices(self, include_blank=False)
|
||||||
|
@ -2,10 +2,12 @@ from django.db import backend, transaction
|
|||||||
from django.db.models import signals, get_model
|
from django.db.models import signals, get_model
|
||||||
from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class
|
from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class
|
||||||
from django.db.models.related import RelatedObject
|
from django.db.models.related import RelatedObject
|
||||||
|
from django.utils.text import capfirst
|
||||||
from django.utils.translation import gettext_lazy, string_concat, ngettext
|
from django.utils.translation import gettext_lazy, string_concat, ngettext
|
||||||
from django.utils.functional import curry
|
from django.utils.functional import curry
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django import forms
|
from django import oldforms
|
||||||
|
from django import newforms as forms
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
|
|
||||||
# For Python 2.3
|
# For Python 2.3
|
||||||
@ -256,8 +258,7 @@ class ForeignRelatedObjectsDescriptor(object):
|
|||||||
# Otherwise, just move the named objects into the set.
|
# Otherwise, just move the named objects into the set.
|
||||||
if self.related.field.null:
|
if self.related.field.null:
|
||||||
manager.clear()
|
manager.clear()
|
||||||
for obj in value:
|
manager.add(*value)
|
||||||
manager.add(obj)
|
|
||||||
|
|
||||||
def create_many_related_manager(superclass):
|
def create_many_related_manager(superclass):
|
||||||
"""Creates a manager that subclasses 'superclass' (which is a Manager)
|
"""Creates a manager that subclasses 'superclass' (which is a Manager)
|
||||||
@ -315,12 +316,20 @@ def create_many_related_manager(superclass):
|
|||||||
# join_table: name of the m2m link table
|
# join_table: name of the m2m link table
|
||||||
# source_col_name: the PK colname in join_table for the source object
|
# source_col_name: the PK colname in join_table for the source object
|
||||||
# target_col_name: the PK colname in join_table for the target object
|
# target_col_name: the PK colname in join_table for the target object
|
||||||
# *objs - objects to add
|
# *objs - objects to add. Either object instances, or primary keys of object instances.
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
|
# 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.
|
# 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
|
# 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 = connection.cursor()
|
||||||
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
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, self.join_table, source_col_name,
|
||||||
@ -344,15 +353,21 @@ def create_many_related_manager(superclass):
|
|||||||
# *objs - objects to remove
|
# *objs - objects to remove
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
|
# 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:
|
for obj in objs:
|
||||||
if not isinstance(obj, self.model):
|
if isinstance(obj, self.model):
|
||||||
raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name
|
old_ids.add(obj._get_pk_val())
|
||||||
|
else:
|
||||||
|
old_ids.add(obj)
|
||||||
# Remove the specified objects from the join table
|
# Remove the specified objects from the join table
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
for obj in objs:
|
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||||
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \
|
(self.join_table, source_col_name,
|
||||||
(self.join_table, source_col_name, target_col_name),
|
target_col_name, ",".join(['%s'] * len(old_ids))),
|
||||||
[self._pk_val, obj._get_pk_val()])
|
[self._pk_val] + list(old_ids))
|
||||||
transaction.commit_unless_managed()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
def _clear_items(self, source_col_name):
|
def _clear_items(self, source_col_name):
|
||||||
@ -405,8 +420,7 @@ class ManyRelatedObjectsDescriptor(object):
|
|||||||
|
|
||||||
manager = self.__get__(instance)
|
manager = self.__get__(instance)
|
||||||
manager.clear()
|
manager.clear()
|
||||||
for obj in value:
|
manager.add(*value)
|
||||||
manager.add(obj)
|
|
||||||
|
|
||||||
class ReverseManyRelatedObjectsDescriptor(object):
|
class ReverseManyRelatedObjectsDescriptor(object):
|
||||||
# This class provides the functionality that makes the related-object
|
# This class provides the functionality that makes the related-object
|
||||||
@ -447,8 +461,7 @@ class ReverseManyRelatedObjectsDescriptor(object):
|
|||||||
|
|
||||||
manager = self.__get__(instance)
|
manager = self.__get__(instance)
|
||||||
manager.clear()
|
manager.clear()
|
||||||
for obj in value:
|
manager.add(*value)
|
||||||
manager.add(obj)
|
|
||||||
|
|
||||||
class ForeignKey(RelatedField, Field):
|
class ForeignKey(RelatedField, Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
@ -493,13 +506,13 @@ class ForeignKey(RelatedField, Field):
|
|||||||
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
|
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
|
||||||
else:
|
else:
|
||||||
if self.radio_admin:
|
if self.radio_admin:
|
||||||
field_objs = [forms.RadioSelectField]
|
field_objs = [oldforms.RadioSelectField]
|
||||||
params['ul_class'] = get_ul_class(self.radio_admin)
|
params['ul_class'] = get_ul_class(self.radio_admin)
|
||||||
else:
|
else:
|
||||||
if self.null:
|
if self.null:
|
||||||
field_objs = [forms.NullSelectField]
|
field_objs = [oldforms.NullSelectField]
|
||||||
else:
|
else:
|
||||||
field_objs = [forms.SelectField]
|
field_objs = [oldforms.SelectField]
|
||||||
params['choices'] = self.get_choices_default()
|
params['choices'] = self.get_choices_default()
|
||||||
return field_objs, params
|
return field_objs, params
|
||||||
|
|
||||||
@ -508,7 +521,7 @@ class ForeignKey(RelatedField, Field):
|
|||||||
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
|
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
|
||||||
return rel_field.get_manipulator_field_objs()
|
return rel_field.get_manipulator_field_objs()
|
||||||
else:
|
else:
|
||||||
return [forms.IntegerField]
|
return [oldforms.IntegerField]
|
||||||
|
|
||||||
def get_db_prep_save(self, value):
|
def get_db_prep_save(self, value):
|
||||||
if value == '' or value == None:
|
if value == '' or value == None:
|
||||||
@ -539,6 +552,11 @@ class ForeignKey(RelatedField, Field):
|
|||||||
def contribute_to_related_class(self, cls, related):
|
def contribute_to_related_class(self, cls, related):
|
||||||
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.ChoiceField(**defaults)
|
||||||
|
|
||||||
class OneToOneField(RelatedField, IntegerField):
|
class OneToOneField(RelatedField, IntegerField):
|
||||||
def __init__(self, to, to_field=None, **kwargs):
|
def __init__(self, to, to_field=None, **kwargs):
|
||||||
try:
|
try:
|
||||||
@ -581,13 +599,13 @@ class OneToOneField(RelatedField, IntegerField):
|
|||||||
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
|
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
|
||||||
else:
|
else:
|
||||||
if self.radio_admin:
|
if self.radio_admin:
|
||||||
field_objs = [forms.RadioSelectField]
|
field_objs = [oldforms.RadioSelectField]
|
||||||
params['ul_class'] = get_ul_class(self.radio_admin)
|
params['ul_class'] = get_ul_class(self.radio_admin)
|
||||||
else:
|
else:
|
||||||
if self.null:
|
if self.null:
|
||||||
field_objs = [forms.NullSelectField]
|
field_objs = [oldforms.NullSelectField]
|
||||||
else:
|
else:
|
||||||
field_objs = [forms.SelectField]
|
field_objs = [oldforms.SelectField]
|
||||||
params['choices'] = self.get_choices_default()
|
params['choices'] = self.get_choices_default()
|
||||||
return field_objs, params
|
return field_objs, params
|
||||||
|
|
||||||
@ -600,6 +618,11 @@ class OneToOneField(RelatedField, IntegerField):
|
|||||||
if not cls._meta.one_to_one_field:
|
if not cls._meta.one_to_one_field:
|
||||||
cls._meta.one_to_one_field = self
|
cls._meta.one_to_one_field = self
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.ChoiceField(**kwargs)
|
||||||
|
|
||||||
class ManyToManyField(RelatedField, Field):
|
class ManyToManyField(RelatedField, Field):
|
||||||
def __init__(self, to, **kwargs):
|
def __init__(self, to, **kwargs):
|
||||||
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
|
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
|
||||||
@ -610,6 +633,7 @@ class ManyToManyField(RelatedField, Field):
|
|||||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||||
raw_id_admin=kwargs.pop('raw_id_admin', False),
|
raw_id_admin=kwargs.pop('raw_id_admin', False),
|
||||||
symmetrical=kwargs.pop('symmetrical', True))
|
symmetrical=kwargs.pop('symmetrical', True))
|
||||||
|
self.db_table = kwargs.pop('db_table', None)
|
||||||
if kwargs["rel"].raw_id_admin:
|
if kwargs["rel"].raw_id_admin:
|
||||||
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
|
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
|
||||||
Field.__init__(self, **kwargs)
|
Field.__init__(self, **kwargs)
|
||||||
@ -622,16 +646,19 @@ class ManyToManyField(RelatedField, Field):
|
|||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
if self.rel.raw_id_admin:
|
if self.rel.raw_id_admin:
|
||||||
return [forms.RawIdAdminField]
|
return [oldforms.RawIdAdminField]
|
||||||
else:
|
else:
|
||||||
choices = self.get_choices_default()
|
choices = self.get_choices_default()
|
||||||
return [curry(forms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
||||||
|
|
||||||
def get_choices_default(self):
|
def get_choices_default(self):
|
||||||
return Field.get_choices(self, include_blank=False)
|
return Field.get_choices(self, include_blank=False)
|
||||||
|
|
||||||
def _get_m2m_db_table(self, opts):
|
def _get_m2m_db_table(self, opts):
|
||||||
"Function that can be curried to provide the m2m table name for this relation"
|
"Function that can be curried to provide the m2m table name for this relation"
|
||||||
|
if self.db_table:
|
||||||
|
return self.db_table
|
||||||
|
else:
|
||||||
return '%s_%s' % (opts.db_table, self.name)
|
return '%s_%s' % (opts.db_table, self.name)
|
||||||
|
|
||||||
def _get_m2m_column_name(self, related):
|
def _get_m2m_column_name(self, related):
|
||||||
@ -706,6 +733,19 @@ class ManyToManyField(RelatedField, Field):
|
|||||||
def set_attributes_from_rel(self):
|
def set_attributes_from_rel(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def value_from_object(self, obj):
|
||||||
|
"Returns the value of this field in the given model instance."
|
||||||
|
return getattr(obj, self.attname).all()
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
# If initial is passed in, it's a list of related objects, but the
|
||||||
|
# MultipleChoiceField takes a list of IDs.
|
||||||
|
if kwargs.get('initial') is not None:
|
||||||
|
kwargs['initial'] = [i._get_pk_val() for i in kwargs['initial']]
|
||||||
|
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.MultipleChoiceField(**defaults)
|
||||||
|
|
||||||
class ManyToOneRel(object):
|
class ManyToOneRel(object):
|
||||||
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
||||||
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
|
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet, EmptyQuerySet
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.db.models.fields import FieldDoesNotExist
|
from django.db.models.fields import FieldDoesNotExist
|
||||||
@ -42,12 +42,18 @@ class Manager(object):
|
|||||||
# PROXIES TO QUERYSET #
|
# PROXIES TO QUERYSET #
|
||||||
#######################
|
#######################
|
||||||
|
|
||||||
|
def get_empty_query_set(self):
|
||||||
|
return EmptyQuerySet(self.model)
|
||||||
|
|
||||||
def get_query_set(self):
|
def get_query_set(self):
|
||||||
"""Returns a new QuerySet object. Subclasses can override this method
|
"""Returns a new QuerySet object. Subclasses can override this method
|
||||||
to easily customise the behaviour of the Manager.
|
to easily customise the behaviour of the Manager.
|
||||||
"""
|
"""
|
||||||
return QuerySet(self.model)
|
return QuerySet(self.model)
|
||||||
|
|
||||||
|
def none(self):
|
||||||
|
return self.get_empty_query_set()
|
||||||
|
|
||||||
def all(self):
|
def all(self):
|
||||||
return self.get_query_set()
|
return self.get_query_set()
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django import forms
|
from django import oldforms
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.db.models.fields import FileField, AutoField
|
from django.db.models.fields import FileField, AutoField
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
@ -40,7 +40,7 @@ class ManipulatorDescriptor(object):
|
|||||||
self.man._prepare(model)
|
self.man._prepare(model)
|
||||||
return self.man
|
return self.man
|
||||||
|
|
||||||
class AutomaticManipulator(forms.Manipulator):
|
class AutomaticManipulator(oldforms.Manipulator):
|
||||||
def _prepare(cls, model):
|
def _prepare(cls, model):
|
||||||
cls.model = model
|
cls.model = model
|
||||||
cls.manager = model._default_manager
|
cls.manager = model._default_manager
|
||||||
@ -76,7 +76,7 @@ class AutomaticManipulator(forms.Manipulator):
|
|||||||
|
|
||||||
# Add field for ordering.
|
# Add field for ordering.
|
||||||
if self.change and self.opts.get_ordered_objects():
|
if self.change and self.opts.get_ordered_objects():
|
||||||
self.fields.append(forms.CommaSeparatedIntegerField(field_name="order_"))
|
self.fields.append(oldforms.CommaSeparatedIntegerField(field_name="order_"))
|
||||||
|
|
||||||
def save(self, new_data):
|
def save(self, new_data):
|
||||||
# TODO: big cleanup when core fields go -> use recursive manipulators.
|
# TODO: big cleanup when core fields go -> use recursive manipulators.
|
||||||
@ -308,7 +308,7 @@ def manipulator_validator_unique_together(field_name_list, opts, self, field_dat
|
|||||||
def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_type, self, field_data, all_data):
|
def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_type, self, field_data, all_data):
|
||||||
from django.db.models.fields.related import ManyToOneRel
|
from django.db.models.fields.related import ManyToOneRel
|
||||||
date_str = all_data.get(date_field.get_manipulator_field_names('')[0], None)
|
date_str = all_data.get(date_field.get_manipulator_field_names('')[0], None)
|
||||||
date_val = forms.DateField.html2python(date_str)
|
date_val = oldforms.DateField.html2python(date_str)
|
||||||
if date_val is None:
|
if date_val is None:
|
||||||
return # Date was invalid. This will be caught by another validator.
|
return # Date was invalid. This will be caught by another validator.
|
||||||
lookup_kwargs = {'%s__year' % date_field.name: date_val.year}
|
lookup_kwargs = {'%s__year' % date_field.name: date_val.year}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.db import backend, connection, transaction
|
from django.db import backend, connection, transaction
|
||||||
from django.db.models.fields import DateField, FieldDoesNotExist
|
from django.db.models.fields import DateField, FieldDoesNotExist
|
||||||
|
from django.db.models.fields.generic import GenericRelation
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
@ -25,6 +26,9 @@ QUERY_TERMS = (
|
|||||||
# Larger values are slightly faster at the expense of more storage space.
|
# Larger values are slightly faster at the expense of more storage space.
|
||||||
GET_ITERATOR_CHUNK_SIZE = 100
|
GET_ITERATOR_CHUNK_SIZE = 100
|
||||||
|
|
||||||
|
class EmptyResultSet(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# HELPER FUNCTIONS #
|
# HELPER FUNCTIONS #
|
||||||
####################
|
####################
|
||||||
@ -168,7 +172,12 @@ class QuerySet(object):
|
|||||||
extra_select = self._select.items()
|
extra_select = self._select.items()
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
select, sql, params = self._get_sql_clause()
|
select, sql, params = self._get_sql_clause()
|
||||||
|
except EmptyResultSet:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||||
fill_cache = self._select_related
|
fill_cache = self._select_related
|
||||||
index_end = len(self.model._meta.fields)
|
index_end = len(self.model._meta.fields)
|
||||||
@ -192,7 +201,12 @@ class QuerySet(object):
|
|||||||
counter._offset = None
|
counter._offset = None
|
||||||
counter._limit = None
|
counter._limit = None
|
||||||
counter._select_related = False
|
counter._select_related = False
|
||||||
|
|
||||||
|
try:
|
||||||
select, sql, params = counter._get_sql_clause()
|
select, sql, params = counter._get_sql_clause()
|
||||||
|
except EmptyResultSet:
|
||||||
|
return 0
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
if self._distinct:
|
if self._distinct:
|
||||||
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
|
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
|
||||||
@ -523,7 +537,12 @@ class ValuesQuerySet(QuerySet):
|
|||||||
field_names = [f.attname for f in self.model._meta.fields]
|
field_names = [f.attname for f in self.model._meta.fields]
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
select, sql, params = self._get_sql_clause()
|
select, sql, params = self._get_sql_clause()
|
||||||
|
except EmptyResultSet:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
|
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
|
||||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||||
while 1:
|
while 1:
|
||||||
@ -545,7 +564,12 @@ class DateQuerySet(QuerySet):
|
|||||||
if self._field.null:
|
if self._field.null:
|
||||||
self._where.append('%s.%s IS NOT NULL' % \
|
self._where.append('%s.%s IS NOT NULL' % \
|
||||||
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
|
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
|
||||||
|
|
||||||
|
try:
|
||||||
select, sql, params = self._get_sql_clause()
|
select, sql, params = self._get_sql_clause()
|
||||||
|
except EmptyResultSet:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \
|
sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \
|
||||||
(backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
|
(backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
|
||||||
backend.quote_name(self._field.column))), sql, self._order)
|
backend.quote_name(self._field.column))), sql, self._order)
|
||||||
@ -563,6 +587,25 @@ class DateQuerySet(QuerySet):
|
|||||||
c._order = self._order
|
c._order = self._order
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
class EmptyQuerySet(QuerySet):
|
||||||
|
def __init__(self, model=None):
|
||||||
|
super(EmptyQuerySet, self).__init__(model)
|
||||||
|
self._result_cache = []
|
||||||
|
|
||||||
|
def iterator(self):
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _clone(self, klass=None, **kwargs):
|
||||||
|
c = super(EmptyQuerySet, self)._clone(klass, **kwargs)
|
||||||
|
c._result_cache = []
|
||||||
|
return c
|
||||||
|
|
||||||
class QOperator(object):
|
class QOperator(object):
|
||||||
"Base class for QAnd and QOr"
|
"Base class for QAnd and QOr"
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
@ -571,10 +614,14 @@ class QOperator(object):
|
|||||||
def get_sql(self, opts):
|
def get_sql(self, opts):
|
||||||
joins, where, params = SortedDict(), [], []
|
joins, where, params = SortedDict(), [], []
|
||||||
for val in self.args:
|
for val in self.args:
|
||||||
|
try:
|
||||||
joins2, where2, params2 = val.get_sql(opts)
|
joins2, where2, params2 = val.get_sql(opts)
|
||||||
joins.update(joins2)
|
joins.update(joins2)
|
||||||
where.extend(where2)
|
where.extend(where2)
|
||||||
params.extend(params2)
|
params.extend(params2)
|
||||||
|
except EmptyResultSet:
|
||||||
|
if not isinstance(self, QOr):
|
||||||
|
raise EmptyResultSet
|
||||||
if where:
|
if where:
|
||||||
return joins, ['(%s)' % self.operator.join(where)], params
|
return joins, ['(%s)' % self.operator.join(where)], params
|
||||||
return joins, [], params
|
return joins, [], params
|
||||||
@ -628,8 +675,11 @@ class QNot(Q):
|
|||||||
self.q = q
|
self.q = q
|
||||||
|
|
||||||
def get_sql(self, opts):
|
def get_sql(self, opts):
|
||||||
|
try:
|
||||||
joins, where, params = self.q.get_sql(opts)
|
joins, where, params = self.q.get_sql(opts)
|
||||||
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
||||||
|
except EmptyResultSet:
|
||||||
|
return SortedDict(), [], []
|
||||||
return joins, where2, params
|
return joins, where2, params
|
||||||
|
|
||||||
def get_where_clause(lookup_type, table_prefix, field_name, value):
|
def get_where_clause(lookup_type, table_prefix, field_name, value):
|
||||||
@ -641,7 +691,11 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
if lookup_type == 'in':
|
if lookup_type == 'in':
|
||||||
return '%s%s IN (%s)' % (table_prefix, field_name, ','.join(['%s' for v in value]))
|
in_string = ','.join(['%s' for id in value])
|
||||||
|
if in_string:
|
||||||
|
return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
|
||||||
|
else:
|
||||||
|
raise EmptyResultSet
|
||||||
elif lookup_type == 'range':
|
elif lookup_type == 'range':
|
||||||
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
|
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
|
||||||
elif lookup_type in ('year', 'month', 'day'):
|
elif lookup_type in ('year', 'month', 'day'):
|
||||||
@ -926,6 +980,7 @@ def delete_objects(seen_objs):
|
|||||||
|
|
||||||
pk_list = [pk for pk,instance in seen_objs[cls]]
|
pk_list = [pk for pk,instance in seen_objs[cls]]
|
||||||
for related in cls._meta.get_all_related_many_to_many_objects():
|
for related in cls._meta.get_all_related_many_to_many_objects():
|
||||||
|
if not isinstance(related.field, GenericRelation):
|
||||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||||
(qn(related.field.m2m_db_table()),
|
(qn(related.field.m2m_db_table()),
|
||||||
@ -933,11 +988,18 @@ def delete_objects(seen_objs):
|
|||||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
||||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
||||||
for f in cls._meta.many_to_many:
|
for f in cls._meta.many_to_many:
|
||||||
|
if isinstance(f, GenericRelation):
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
|
||||||
|
args_extra = [ContentType.objects.get_for_model(cls).id]
|
||||||
|
else:
|
||||||
|
query_extra = ''
|
||||||
|
args_extra = []
|
||||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||||
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
|
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
|
||||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra,
|
||||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
|
||||||
for field in cls._meta.fields:
|
for field in cls._meta.fields:
|
||||||
if field.rel and field.null and field.rel.to in seen_objs:
|
if field.rel and field.null and field.rel.to in seen_objs:
|
||||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,5 +13,5 @@ TODO:
|
|||||||
from util import ValidationError
|
from util import ValidationError
|
||||||
from widgets import *
|
from widgets import *
|
||||||
from fields import *
|
from fields import *
|
||||||
from forms import Form
|
from forms import *
|
||||||
from models import *
|
from models import *
|
||||||
|
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 django.utils.translation import gettext
|
||||||
from util import ValidationError, smart_unicode
|
from util import ErrorList, ValidationError, smart_unicode
|
||||||
from widgets import TextInput, CheckboxInput, Select, SelectMultiple
|
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
@ -12,10 +12,12 @@ import time
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
'Field', 'CharField', 'IntegerField',
|
'Field', 'CharField', 'IntegerField',
|
||||||
'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
|
'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
|
||||||
|
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
|
||||||
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
|
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
|
||||||
'RegexField', 'EmailField', 'URLField', 'BooleanField',
|
'RegexField', 'EmailField', 'URLField', 'BooleanField',
|
||||||
'ChoiceField', 'MultipleChoiceField',
|
'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
|
||||||
'ComboField',
|
'ComboField', 'MultiValueField',
|
||||||
|
'SplitDateTimeField',
|
||||||
)
|
)
|
||||||
|
|
||||||
# These values, if given to to_python(), will trigger the self.required check.
|
# These values, if given to to_python(), will trigger the self.required check.
|
||||||
@ -28,15 +30,37 @@ except NameError:
|
|||||||
|
|
||||||
class Field(object):
|
class Field(object):
|
||||||
widget = TextInput # Default widget to use when rendering this type of Field.
|
widget = TextInput # Default widget to use when rendering this type of Field.
|
||||||
|
hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
|
||||||
|
|
||||||
# Tracks each time a Field instance is created. Used to retain order.
|
# Tracks each time a Field instance is created. Used to retain order.
|
||||||
creation_counter = 0
|
creation_counter = 0
|
||||||
|
|
||||||
def __init__(self, required=True, widget=None):
|
def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None):
|
||||||
self.required = required
|
# required -- Boolean that specifies whether the field is required.
|
||||||
|
# True by default.
|
||||||
|
# widget -- A Widget class, or instance of a Widget class, that should be
|
||||||
|
# used for this Field when displaying it. Each Field has a default
|
||||||
|
# Widget that it'll use if you don't specify this. In most cases,
|
||||||
|
# the default widget is TextInput.
|
||||||
|
# label -- A verbose name for this field, for use in displaying this field in
|
||||||
|
# a form. By default, Django will use a "pretty" version of the form
|
||||||
|
# field name, if the Field is part of a Form.
|
||||||
|
# initial -- A value to use in this Field's initial display. This value is
|
||||||
|
# *not* used as a fallback if data isn't given.
|
||||||
|
# help_text -- An optional string to use as "help text" for this Field.
|
||||||
|
if label is not None:
|
||||||
|
label = smart_unicode(label)
|
||||||
|
self.required, self.label, self.initial = required, label, initial
|
||||||
|
self.help_text = help_text
|
||||||
widget = widget or self.widget
|
widget = widget or self.widget
|
||||||
if isinstance(widget, type):
|
if isinstance(widget, type):
|
||||||
widget = widget()
|
widget = widget()
|
||||||
|
|
||||||
|
# Hook into self.widget_attrs() for any Field-specific HTML attributes.
|
||||||
|
extra_attrs = self.widget_attrs(widget)
|
||||||
|
if extra_attrs:
|
||||||
|
widget.attrs.update(extra_attrs)
|
||||||
|
|
||||||
self.widget = widget
|
self.widget = widget
|
||||||
|
|
||||||
# Increase the creation counter, and save our local copy.
|
# Increase the creation counter, and save our local copy.
|
||||||
@ -54,15 +78,24 @@ class Field(object):
|
|||||||
raise ValidationError(gettext(u'This field is required.'))
|
raise ValidationError(gettext(u'This field is required.'))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def widget_attrs(self, widget):
|
||||||
|
"""
|
||||||
|
Given a Widget instance (*not* a Widget class), returns a dictionary of
|
||||||
|
any HTML attributes that should be added to the Widget, based on this
|
||||||
|
Field.
|
||||||
|
"""
|
||||||
|
return {}
|
||||||
|
|
||||||
class CharField(Field):
|
class CharField(Field):
|
||||||
def __init__(self, max_length=None, min_length=None, required=True, widget=None):
|
def __init__(self, max_length=None, min_length=None, *args, **kwargs):
|
||||||
Field.__init__(self, required, widget)
|
|
||||||
self.max_length, self.min_length = max_length, min_length
|
self.max_length, self.min_length = max_length, min_length
|
||||||
|
super(CharField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"Validates max_length and min_length. Returns a Unicode object."
|
"Validates max_length and min_length. Returns a Unicode object."
|
||||||
Field.clean(self, value)
|
super(CharField, self).clean(value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
value = smart_unicode(value)
|
value = smart_unicode(value)
|
||||||
if self.max_length is not None and len(value) > self.max_length:
|
if self.max_length is not None and len(value) > self.max_length:
|
||||||
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||||
@ -70,19 +103,32 @@ class CharField(Field):
|
|||||||
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
|
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def widget_attrs(self, widget):
|
||||||
|
if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
|
||||||
|
return {'maxlength': str(self.max_length)}
|
||||||
|
|
||||||
class IntegerField(Field):
|
class IntegerField(Field):
|
||||||
|
def __init__(self, max_value=None, min_value=None, *args, **kwargs):
|
||||||
|
self.max_value, self.min_value = max_value, min_value
|
||||||
|
super(IntegerField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that int() can be called on the input. Returns the result
|
Validates that int() can be called on the input. Returns the result
|
||||||
of int().
|
of int(). Returns None for empty values.
|
||||||
"""
|
"""
|
||||||
super(IntegerField, self).clean(value)
|
super(IntegerField, self).clean(value)
|
||||||
if not self.required and value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
return u''
|
return None
|
||||||
try:
|
try:
|
||||||
return int(value)
|
value = int(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
raise ValidationError(gettext(u'Enter a whole number.'))
|
raise ValidationError(gettext(u'Enter a whole number.'))
|
||||||
|
if self.max_value is not None and value > self.max_value:
|
||||||
|
raise ValidationError(gettext(u'Ensure this value is less than or equal to %s.') % self.max_value)
|
||||||
|
if self.min_value is not None and value < self.min_value:
|
||||||
|
raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value)
|
||||||
|
return value
|
||||||
|
|
||||||
DEFAULT_DATE_INPUT_FORMATS = (
|
DEFAULT_DATE_INPUT_FORMATS = (
|
||||||
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
|
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
|
||||||
@ -93,8 +139,8 @@ DEFAULT_DATE_INPUT_FORMATS = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
class DateField(Field):
|
class DateField(Field):
|
||||||
def __init__(self, input_formats=None, required=True, widget=None):
|
def __init__(self, input_formats=None, *args, **kwargs):
|
||||||
Field.__init__(self, required, widget)
|
super(DateField, self).__init__(*args, **kwargs)
|
||||||
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
@ -102,7 +148,7 @@ class DateField(Field):
|
|||||||
Validates that the input can be converted to a date. Returns a Python
|
Validates that the input can be converted to a date. Returns a Python
|
||||||
datetime.date object.
|
datetime.date object.
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
super(DateField, self).clean(value)
|
||||||
if value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
return None
|
return None
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
@ -116,6 +162,33 @@ class DateField(Field):
|
|||||||
continue
|
continue
|
||||||
raise ValidationError(gettext(u'Enter a valid date.'))
|
raise ValidationError(gettext(u'Enter a valid date.'))
|
||||||
|
|
||||||
|
DEFAULT_TIME_INPUT_FORMATS = (
|
||||||
|
'%H:%M:%S', # '14:30:59'
|
||||||
|
'%H:%M', # '14:30'
|
||||||
|
)
|
||||||
|
|
||||||
|
class TimeField(Field):
|
||||||
|
def __init__(self, input_formats=None, *args, **kwargs):
|
||||||
|
super(TimeField, self).__init__(*args, **kwargs)
|
||||||
|
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
"""
|
||||||
|
Validates that the input can be converted to a time. Returns a Python
|
||||||
|
datetime.time object.
|
||||||
|
"""
|
||||||
|
super(TimeField, self).clean(value)
|
||||||
|
if value in EMPTY_VALUES:
|
||||||
|
return None
|
||||||
|
if isinstance(value, datetime.time):
|
||||||
|
return value
|
||||||
|
for format in self.input_formats:
|
||||||
|
try:
|
||||||
|
return datetime.time(*time.strptime(value, format)[3:6])
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
raise ValidationError(gettext(u'Enter a valid time.'))
|
||||||
|
|
||||||
DEFAULT_DATETIME_INPUT_FORMATS = (
|
DEFAULT_DATETIME_INPUT_FORMATS = (
|
||||||
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
|
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
|
||||||
'%Y-%m-%d %H:%M', # '2006-10-25 14:30'
|
'%Y-%m-%d %H:%M', # '2006-10-25 14:30'
|
||||||
@ -129,8 +202,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
class DateTimeField(Field):
|
class DateTimeField(Field):
|
||||||
def __init__(self, input_formats=None, required=True, widget=None):
|
def __init__(self, input_formats=None, *args, **kwargs):
|
||||||
Field.__init__(self, required, widget)
|
super(DateTimeField, self).__init__(*args, **kwargs)
|
||||||
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
@ -138,7 +211,7 @@ class DateTimeField(Field):
|
|||||||
Validates that the input can be converted to a datetime. Returns a
|
Validates that the input can be converted to a datetime. Returns a
|
||||||
Python datetime.datetime object.
|
Python datetime.datetime object.
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
super(DateTimeField, self).clean(value)
|
||||||
if value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
return None
|
return None
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
@ -153,16 +226,17 @@ class DateTimeField(Field):
|
|||||||
raise ValidationError(gettext(u'Enter a valid date/time.'))
|
raise ValidationError(gettext(u'Enter a valid date/time.'))
|
||||||
|
|
||||||
class RegexField(Field):
|
class RegexField(Field):
|
||||||
def __init__(self, regex, error_message=None, required=True, widget=None):
|
def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
regex can be either a string or a compiled regular expression object.
|
regex can be either a string or a compiled regular expression object.
|
||||||
error_message is an optional error message to use, if
|
error_message is an optional error message to use, if
|
||||||
'Enter a valid value' is too generic for you.
|
'Enter a valid value' is too generic for you.
|
||||||
"""
|
"""
|
||||||
Field.__init__(self, required, widget)
|
super(RegexField, self).__init__(*args, **kwargs)
|
||||||
if isinstance(regex, basestring):
|
if isinstance(regex, basestring):
|
||||||
regex = re.compile(regex)
|
regex = re.compile(regex)
|
||||||
self.regex = regex
|
self.regex = regex
|
||||||
|
self.max_length, self.min_length = max_length, min_length
|
||||||
self.error_message = error_message or gettext(u'Enter a valid value.')
|
self.error_message = error_message or gettext(u'Enter a valid value.')
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
@ -170,11 +244,16 @@ class RegexField(Field):
|
|||||||
Validates that the input matches the regular expression. Returns a
|
Validates that the input matches the regular expression. Returns a
|
||||||
Unicode object.
|
Unicode object.
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
super(RegexField, self).clean(value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES:
|
||||||
|
value = u''
|
||||||
value = smart_unicode(value)
|
value = smart_unicode(value)
|
||||||
if not self.required and value == u'':
|
if value == u'':
|
||||||
return value
|
return value
|
||||||
|
if self.max_length is not None and len(value) > self.max_length:
|
||||||
|
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||||
|
if self.min_length is not None and len(value) < self.min_length:
|
||||||
|
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
|
||||||
if not self.regex.search(value):
|
if not self.regex.search(value):
|
||||||
raise ValidationError(self.error_message)
|
raise ValidationError(self.error_message)
|
||||||
return value
|
return value
|
||||||
@ -185,8 +264,9 @@ email_re = re.compile(
|
|||||||
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
|
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
|
||||||
|
|
||||||
class EmailField(RegexField):
|
class EmailField(RegexField):
|
||||||
def __init__(self, required=True, widget=None):
|
def __init__(self, max_length=None, min_length=None, *args, **kwargs):
|
||||||
RegexField.__init__(self, email_re, gettext(u'Enter a valid e-mail address.'), required, widget)
|
RegexField.__init__(self, email_re, max_length, min_length,
|
||||||
|
gettext(u'Enter a valid e-mail address.'), *args, **kwargs)
|
||||||
|
|
||||||
url_re = re.compile(
|
url_re = re.compile(
|
||||||
r'^https?://' # http:// or https://
|
r'^https?://' # http:// or https://
|
||||||
@ -202,14 +282,16 @@ except ImportError:
|
|||||||
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
||||||
|
|
||||||
class URLField(RegexField):
|
class URLField(RegexField):
|
||||||
def __init__(self, required=True, verify_exists=False, widget=None,
|
def __init__(self, max_length=None, min_length=None, verify_exists=False,
|
||||||
validator_user_agent=URL_VALIDATOR_USER_AGENT):
|
validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
|
||||||
RegexField.__init__(self, url_re, gettext(u'Enter a valid URL.'), required, widget)
|
super(URLField, self).__init__(url_re, max_length, min_length, gettext(u'Enter a valid URL.'), *args, **kwargs)
|
||||||
self.verify_exists = verify_exists
|
self.verify_exists = verify_exists
|
||||||
self.user_agent = validator_user_agent
|
self.user_agent = validator_user_agent
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
value = RegexField.clean(self, value)
|
value = super(URLField, self).clean(value)
|
||||||
|
if value == u'':
|
||||||
|
return value
|
||||||
if self.verify_exists:
|
if self.verify_exists:
|
||||||
import urllib2
|
import urllib2
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -234,24 +316,43 @@ class BooleanField(Field):
|
|||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"Returns a Python boolean object."
|
"Returns a Python boolean object."
|
||||||
Field.clean(self, value)
|
super(BooleanField, self).clean(value)
|
||||||
return bool(value)
|
return bool(value)
|
||||||
|
|
||||||
|
class NullBooleanField(BooleanField):
|
||||||
|
"""
|
||||||
|
A field whose valid values are None, True and False. Invalid values are
|
||||||
|
cleaned to None.
|
||||||
|
"""
|
||||||
|
widget = NullBooleanSelect
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
return {True: True, False: False}.get(value, None)
|
||||||
|
|
||||||
class ChoiceField(Field):
|
class ChoiceField(Field):
|
||||||
def __init__(self, choices=(), required=True, widget=Select):
|
def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None, help_text=None):
|
||||||
if isinstance(widget, type):
|
super(ChoiceField, self).__init__(required, widget, label, initial, help_text)
|
||||||
widget = widget(choices=choices)
|
|
||||||
Field.__init__(self, required, widget)
|
|
||||||
self.choices = choices
|
self.choices = choices
|
||||||
|
|
||||||
|
def _get_choices(self):
|
||||||
|
return self._choices
|
||||||
|
|
||||||
|
def _set_choices(self, value):
|
||||||
|
# Setting choices also sets the choices on the widget.
|
||||||
|
self._choices = value
|
||||||
|
self.widget.choices = value
|
||||||
|
|
||||||
|
choices = property(_get_choices, _set_choices)
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input is in self.choices.
|
Validates that the input is in self.choices.
|
||||||
"""
|
"""
|
||||||
value = Field.clean(self, value)
|
value = super(ChoiceField, self).clean(value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES:
|
||||||
|
value = u''
|
||||||
value = smart_unicode(value)
|
value = smart_unicode(value)
|
||||||
if not self.required and value == u'':
|
if value == u'':
|
||||||
return value
|
return value
|
||||||
valid_values = set([str(k) for k, v in self.choices])
|
valid_values = set([str(k) for k, v in self.choices])
|
||||||
if value not in valid_values:
|
if value not in valid_values:
|
||||||
@ -259,8 +360,10 @@ class ChoiceField(Field):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
class MultipleChoiceField(ChoiceField):
|
class MultipleChoiceField(ChoiceField):
|
||||||
def __init__(self, choices=(), required=True, widget=SelectMultiple):
|
hidden_widget = MultipleHiddenInput
|
||||||
ChoiceField.__init__(self, choices, required, widget)
|
|
||||||
|
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None, help_text=None):
|
||||||
|
super(MultipleChoiceField, self).__init__(choices, required, widget, label, initial, help_text)
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
@ -284,8 +387,11 @@ class MultipleChoiceField(ChoiceField):
|
|||||||
return new_value
|
return new_value
|
||||||
|
|
||||||
class ComboField(Field):
|
class ComboField(Field):
|
||||||
def __init__(self, fields=(), required=True, widget=None):
|
"""
|
||||||
Field.__init__(self, required, widget)
|
A Field whose clean() method calls multiple Field clean() methods.
|
||||||
|
"""
|
||||||
|
def __init__(self, fields=(), *args, **kwargs):
|
||||||
|
super(ComboField, self).__init__(*args, **kwargs)
|
||||||
# Set 'required' to False on the individual fields, because the
|
# Set 'required' to False on the individual fields, because the
|
||||||
# required validation will be handled by ComboField, not by those
|
# required validation will be handled by ComboField, not by those
|
||||||
# individual fields.
|
# individual fields.
|
||||||
@ -298,7 +404,88 @@ class ComboField(Field):
|
|||||||
Validates the given value against all of self.fields, which is a
|
Validates the given value against all of self.fields, which is a
|
||||||
list of Field instances.
|
list of Field instances.
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
super(ComboField, self).clean(value)
|
||||||
for field in self.fields:
|
for field in self.fields:
|
||||||
value = field.clean(value)
|
value = field.clean(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
class MultiValueField(Field):
|
||||||
|
"""
|
||||||
|
A Field that is composed of multiple Fields.
|
||||||
|
|
||||||
|
Its clean() method takes a "decompressed" list of values. Each value in
|
||||||
|
this list is cleaned by the corresponding field -- the first value is
|
||||||
|
cleaned by the first field, the second value is cleaned by the second
|
||||||
|
field, etc. Once all fields are cleaned, the list of clean values is
|
||||||
|
"compressed" into a single value.
|
||||||
|
|
||||||
|
Subclasses should implement compress(), which specifies how a list of
|
||||||
|
valid values should be converted to a single value. Subclasses should not
|
||||||
|
have to implement clean().
|
||||||
|
|
||||||
|
You'll probably want to use this with MultiWidget.
|
||||||
|
"""
|
||||||
|
def __init__(self, fields=(), *args, **kwargs):
|
||||||
|
super(MultiValueField, self).__init__(*args, **kwargs)
|
||||||
|
# Set 'required' to False on the individual fields, because the
|
||||||
|
# required validation will be handled by MultiValueField, not by those
|
||||||
|
# individual fields.
|
||||||
|
for f in fields:
|
||||||
|
f.required = False
|
||||||
|
self.fields = fields
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
"""
|
||||||
|
Validates every value in the given list. A value is validated against
|
||||||
|
the corresponding Field in self.fields.
|
||||||
|
|
||||||
|
For example, if this MultiValueField was instantiated with
|
||||||
|
fields=(DateField(), TimeField()), clean() would call
|
||||||
|
DateField.clean(value[0]) and TimeField.clean(value[1]).
|
||||||
|
"""
|
||||||
|
clean_data = []
|
||||||
|
errors = ErrorList()
|
||||||
|
if self.required and not value:
|
||||||
|
raise ValidationError(gettext(u'This field is required.'))
|
||||||
|
elif not self.required and not value:
|
||||||
|
return self.compress([])
|
||||||
|
if not isinstance(value, (list, tuple)):
|
||||||
|
raise ValidationError(gettext(u'Enter a list of values.'))
|
||||||
|
for i, field in enumerate(self.fields):
|
||||||
|
try:
|
||||||
|
field_value = value[i]
|
||||||
|
except KeyError:
|
||||||
|
field_value = None
|
||||||
|
if self.required and field_value in EMPTY_VALUES:
|
||||||
|
raise ValidationError(gettext(u'This field is required.'))
|
||||||
|
try:
|
||||||
|
clean_data.append(field.clean(field_value))
|
||||||
|
except ValidationError, e:
|
||||||
|
# Collect all validation errors in a single list, which we'll
|
||||||
|
# raise at the end of clean(), rather than raising a single
|
||||||
|
# exception for the first error we encounter.
|
||||||
|
errors.extend(e.messages)
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
return self.compress(clean_data)
|
||||||
|
|
||||||
|
def compress(self, data_list):
|
||||||
|
"""
|
||||||
|
Returns a single value for the given list of values. The values can be
|
||||||
|
assumed to be valid.
|
||||||
|
|
||||||
|
For example, if this MultiValueField was instantiated with
|
||||||
|
fields=(DateField(), TimeField()), this might return a datetime
|
||||||
|
object created by combining the date and time in data_list.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError('Subclasses must implement this method.')
|
||||||
|
|
||||||
|
class SplitDateTimeField(MultiValueField):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
fields = (DateField(), TimeField())
|
||||||
|
super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
|
||||||
|
|
||||||
|
def compress(self, data_list):
|
||||||
|
if data_list:
|
||||||
|
return datetime.datetime.combine(*data_list)
|
||||||
|
return None
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
Form classes
|
Form classes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict, MultiValueDict
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from fields import Field
|
from fields import Field
|
||||||
from widgets import TextInput, Textarea, HiddenInput
|
from widgets import TextInput, Textarea, HiddenInput, MultipleHiddenInput
|
||||||
from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError
|
from util import flatatt, StrAndUnicode, ErrorDict, ErrorList, ValidationError
|
||||||
|
|
||||||
|
__all__ = ('BaseForm', 'Form')
|
||||||
|
|
||||||
NON_FIELD_ERRORS = '__all__'
|
NON_FIELD_ERRORS = '__all__'
|
||||||
|
|
||||||
@ -24,24 +26,35 @@ class SortedDictFromList(SortedDict):
|
|||||||
self.keyOrder = [d[0] for d in data]
|
self.keyOrder = [d[0] for d in data]
|
||||||
dict.__init__(self, dict(data))
|
dict.__init__(self, dict(data))
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return SortedDictFromList(self.items())
|
||||||
|
|
||||||
class DeclarativeFieldsMetaclass(type):
|
class DeclarativeFieldsMetaclass(type):
|
||||||
"Metaclass that converts Field attributes to a dictionary called 'fields'."
|
"Metaclass that converts Field attributes to a dictionary called 'base_fields'."
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)]
|
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
|
||||||
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
|
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
|
||||||
attrs['fields'] = SortedDictFromList(fields)
|
attrs['base_fields'] = SortedDictFromList(fields)
|
||||||
return type.__new__(cls, name, bases, attrs)
|
return type.__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
class Form(StrAndUnicode):
|
class BaseForm(StrAndUnicode):
|
||||||
"A collection of Fields, plus their associated data."
|
# This is the main implementation of all the Form logic. Note that this
|
||||||
__metaclass__ = DeclarativeFieldsMetaclass
|
# class is different than Form. See the comments by the Form class for more
|
||||||
|
# information. Any improvements to the form API should be made to *this*
|
||||||
def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
|
# class, not to the Form class.
|
||||||
self.ignore_errors = data is None
|
def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):
|
||||||
|
self.is_bound = data is not None
|
||||||
self.data = data or {}
|
self.data = data or {}
|
||||||
self.auto_id = auto_id
|
self.auto_id = auto_id
|
||||||
self.clean_data = None # Stores the data after clean() has been called.
|
self.prefix = prefix
|
||||||
|
self.initial = initial or {}
|
||||||
self.__errors = None # Stores the errors after clean() has been called.
|
self.__errors = None # Stores the errors after clean() has been called.
|
||||||
|
# The base_fields class attribute is the *class-wide* definition of
|
||||||
|
# fields. Because a particular *instance* of the class might want to
|
||||||
|
# alter self.fields, we create self.fields here by copying base_fields.
|
||||||
|
# Instances should always modify self.fields; they should not modify
|
||||||
|
# self.base_fields.
|
||||||
|
self.fields = self.base_fields.copy()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.as_table()
|
return self.as_table()
|
||||||
@ -70,9 +83,18 @@ class Form(StrAndUnicode):
|
|||||||
Returns True if the form has no errors. Otherwise, False. If errors are
|
Returns True if the form has no errors. Otherwise, False. If errors are
|
||||||
being ignored, returns False.
|
being ignored, returns False.
|
||||||
"""
|
"""
|
||||||
return not self.ignore_errors and not bool(self.errors)
|
return self.is_bound and not bool(self.errors)
|
||||||
|
|
||||||
def _html_output(self, normal_row, error_row, row_ender, errors_on_separate_row):
|
def add_prefix(self, field_name):
|
||||||
|
"""
|
||||||
|
Returns the field name with a prefix appended, if this Form has a
|
||||||
|
prefix set.
|
||||||
|
|
||||||
|
Subclasses may wish to override.
|
||||||
|
"""
|
||||||
|
return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name
|
||||||
|
|
||||||
|
def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
|
||||||
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
|
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
|
||||||
top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
|
top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
|
||||||
output, hidden_fields = [], []
|
output, hidden_fields = [], []
|
||||||
@ -86,7 +108,12 @@ class Form(StrAndUnicode):
|
|||||||
else:
|
else:
|
||||||
if errors_on_separate_row and bf_errors:
|
if errors_on_separate_row and bf_errors:
|
||||||
output.append(error_row % bf_errors)
|
output.append(error_row % bf_errors)
|
||||||
output.append(normal_row % {'errors': bf_errors, 'label': bf.label_tag(escape(bf.verbose_name+':')), 'field': bf})
|
label = bf.label and bf.label_tag(escape(bf.label + ':')) or ''
|
||||||
|
if field.help_text:
|
||||||
|
help_text = help_text_html % field.help_text
|
||||||
|
else:
|
||||||
|
help_text = u''
|
||||||
|
output.append(normal_row % {'errors': bf_errors, 'label': label, 'field': unicode(bf), 'help_text': help_text})
|
||||||
if top_errors:
|
if top_errors:
|
||||||
output.insert(0, error_row % top_errors)
|
output.insert(0, error_row % top_errors)
|
||||||
if hidden_fields: # Insert any hidden fields in the last row.
|
if hidden_fields: # Insert any hidden fields in the last row.
|
||||||
@ -101,15 +128,15 @@ class Form(StrAndUnicode):
|
|||||||
|
|
||||||
def as_table(self):
|
def as_table(self):
|
||||||
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
|
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
|
||||||
return self._html_output(u'<tr><td>%(label)s</td><td>%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', True)
|
return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
|
||||||
|
|
||||||
def as_ul(self):
|
def as_ul(self):
|
||||||
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
|
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
|
||||||
return self._html_output(u'<li>%(errors)s%(label)s %(field)s</li>', u'<li>%s</li>', '</li>', False)
|
return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
|
||||||
|
|
||||||
def as_p(self):
|
def as_p(self):
|
||||||
"Returns this form rendered as HTML <p>s."
|
"Returns this form rendered as HTML <p>s."
|
||||||
return self._html_output(u'<p>%(label)s %(field)s</p>', u'<p>%s</p>', '</p>', True)
|
return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'<p>%s</p>', '</p>', u' %s', True)
|
||||||
|
|
||||||
def non_field_errors(self):
|
def non_field_errors(self):
|
||||||
"""
|
"""
|
||||||
@ -123,16 +150,16 @@ class Form(StrAndUnicode):
|
|||||||
"""
|
"""
|
||||||
Cleans all of self.data and populates self.__errors and self.clean_data.
|
Cleans all of self.data and populates self.__errors and self.clean_data.
|
||||||
"""
|
"""
|
||||||
self.clean_data = {}
|
|
||||||
errors = ErrorDict()
|
errors = ErrorDict()
|
||||||
if self.ignore_errors: # Stop further processing.
|
if not self.is_bound: # Stop further processing.
|
||||||
self.__errors = errors
|
self.__errors = errors
|
||||||
return
|
return
|
||||||
|
self.clean_data = {}
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
# value_from_datadict() gets the data from the dictionary.
|
# value_from_datadict() gets the data from the dictionary.
|
||||||
# Each widget type knows how to retrieve its own data, because some
|
# Each widget type knows how to retrieve its own data, because some
|
||||||
# widgets split data over several HTML fields.
|
# widgets split data over several HTML fields.
|
||||||
value = field.widget.value_from_datadict(self.data, name)
|
value = field.widget.value_from_datadict(self.data, self.add_prefix(name))
|
||||||
try:
|
try:
|
||||||
value = field.clean(value)
|
value = field.clean(value)
|
||||||
self.clean_data[name] = value
|
self.clean_data[name] = value
|
||||||
@ -146,7 +173,7 @@ class Form(StrAndUnicode):
|
|||||||
except ValidationError, e:
|
except ValidationError, e:
|
||||||
errors[NON_FIELD_ERRORS] = e.messages
|
errors[NON_FIELD_ERRORS] = e.messages
|
||||||
if errors:
|
if errors:
|
||||||
self.clean_data = None
|
delattr(self, 'clean_data')
|
||||||
self.__errors = errors
|
self.__errors = errors
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
@ -158,12 +185,26 @@ class Form(StrAndUnicode):
|
|||||||
"""
|
"""
|
||||||
return self.clean_data
|
return self.clean_data
|
||||||
|
|
||||||
|
class Form(BaseForm):
|
||||||
|
"A collection of Fields, plus their associated data."
|
||||||
|
# This is a separate class from BaseForm in order to abstract the way
|
||||||
|
# self.fields is specified. This class (Form) is the one that does the
|
||||||
|
# fancy metaclass stuff purely for the semantic sugar -- it allows one
|
||||||
|
# to define a form using declarative syntax.
|
||||||
|
# BaseForm itself has no way of designating self.fields.
|
||||||
|
__metaclass__ = DeclarativeFieldsMetaclass
|
||||||
|
|
||||||
class BoundField(StrAndUnicode):
|
class BoundField(StrAndUnicode):
|
||||||
"A Field plus data"
|
"A Field plus data"
|
||||||
def __init__(self, form, field, name):
|
def __init__(self, form, field, name):
|
||||||
self.form = form
|
self.form = form
|
||||||
self.field = field
|
self.field = field
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.html_name = form.add_prefix(name)
|
||||||
|
if self.field.label is None:
|
||||||
|
self.label = pretty_name(name)
|
||||||
|
else:
|
||||||
|
self.label = self.field.label
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
"Renders this field as an HTML widget."
|
"Renders this field as an HTML widget."
|
||||||
@ -190,7 +231,11 @@ class BoundField(StrAndUnicode):
|
|||||||
auto_id = self.auto_id
|
auto_id = self.auto_id
|
||||||
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
|
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
|
||||||
attrs['id'] = auto_id
|
attrs['id'] = auto_id
|
||||||
return widget.render(self.name, self.data, attrs=attrs)
|
if not self.form.is_bound:
|
||||||
|
data = self.form.initial.get(self.name, self.field.initial)
|
||||||
|
else:
|
||||||
|
data = self.data
|
||||||
|
return widget.render(self.html_name, data, attrs=attrs)
|
||||||
|
|
||||||
def as_text(self, attrs=None):
|
def as_text(self, attrs=None):
|
||||||
"""
|
"""
|
||||||
@ -206,28 +251,29 @@ class BoundField(StrAndUnicode):
|
|||||||
"""
|
"""
|
||||||
Returns a string of HTML for representing this as an <input type="hidden">.
|
Returns a string of HTML for representing this as an <input type="hidden">.
|
||||||
"""
|
"""
|
||||||
return self.as_widget(HiddenInput(), attrs)
|
return self.as_widget(self.field.hidden_widget(), attrs)
|
||||||
|
|
||||||
def _data(self):
|
def _data(self):
|
||||||
"Returns the data for this BoundField, or None if it wasn't given."
|
"""
|
||||||
return self.form.data.get(self.name, None)
|
Returns the data for this BoundField, or None if it wasn't given.
|
||||||
|
"""
|
||||||
|
return self.field.widget.value_from_datadict(self.form.data, self.html_name)
|
||||||
data = property(_data)
|
data = property(_data)
|
||||||
|
|
||||||
def _verbose_name(self):
|
def label_tag(self, contents=None, attrs=None):
|
||||||
return pretty_name(self.name)
|
|
||||||
verbose_name = property(_verbose_name)
|
|
||||||
|
|
||||||
def label_tag(self, contents=None):
|
|
||||||
"""
|
"""
|
||||||
Wraps the given contents in a <label>, if the field has an ID attribute.
|
Wraps the given contents in a <label>, if the field has an ID attribute.
|
||||||
Does not HTML-escape the contents. If contents aren't given, uses the
|
Does not HTML-escape the contents. If contents aren't given, uses the
|
||||||
field's HTML-escaped verbose_name.
|
field's HTML-escaped label.
|
||||||
|
|
||||||
|
If attrs are given, they're used as HTML attributes on the <label> tag.
|
||||||
"""
|
"""
|
||||||
contents = contents or escape(self.verbose_name)
|
contents = contents or escape(self.label)
|
||||||
widget = self.field.widget
|
widget = self.field.widget
|
||||||
id_ = widget.attrs.get('id') or self.auto_id
|
id_ = widget.attrs.get('id') or self.auto_id
|
||||||
if id_:
|
if id_:
|
||||||
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents)
|
attrs = attrs and flatatt(attrs) or ''
|
||||||
|
contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents)
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
def _is_hidden(self):
|
def _is_hidden(self):
|
||||||
@ -242,8 +288,8 @@ class BoundField(StrAndUnicode):
|
|||||||
"""
|
"""
|
||||||
auto_id = self.form.auto_id
|
auto_id = self.form.auto_id
|
||||||
if auto_id and '%s' in str(auto_id):
|
if auto_id and '%s' in str(auto_id):
|
||||||
return str(auto_id) % self.name
|
return str(auto_id) % self.html_name
|
||||||
elif auto_id:
|
elif auto_id:
|
||||||
return self.name
|
return self.html_name
|
||||||
return ''
|
return ''
|
||||||
auto_id = property(_auto_id)
|
auto_id = property(_auto_id)
|
||||||
|
@ -1,13 +1,98 @@
|
|||||||
"""
|
"""
|
||||||
Helper functions for creating Forms from Django models and database field objects.
|
Helper functions for creating Form classes from Django models
|
||||||
|
and database field objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = ('form_for_model', 'form_for_fields')
|
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
|
||||||
|
|
||||||
def form_for_model(model):
|
__all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields')
|
||||||
"Returns a Form instance for the given Django model class."
|
|
||||||
raise NotImplementedError
|
def model_save(self, commit=True):
|
||||||
|
"""
|
||||||
|
Creates and returns model instance according to self.clean_data.
|
||||||
|
|
||||||
|
This method is created for any form_for_model Form.
|
||||||
|
"""
|
||||||
|
if self.errors:
|
||||||
|
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
|
||||||
|
return save_instance(self, self._model(), commit)
|
||||||
|
|
||||||
|
def save_instance(form, instance, commit=True):
|
||||||
|
"""
|
||||||
|
Saves bound Form ``form``'s clean_data into model instance ``instance``.
|
||||||
|
|
||||||
|
Assumes ``form`` has a field for every non-AutoField database field in
|
||||||
|
``instance``. If commit=True, then the changes to ``instance`` will be
|
||||||
|
saved to the database. Returns ``instance``.
|
||||||
|
"""
|
||||||
|
from django.db import models
|
||||||
|
opts = instance.__class__._meta
|
||||||
|
if form.errors:
|
||||||
|
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
||||||
|
clean_data = form.clean_data
|
||||||
|
for f in opts.fields:
|
||||||
|
if isinstance(f, models.AutoField):
|
||||||
|
continue
|
||||||
|
setattr(instance, f.attname, clean_data[f.name])
|
||||||
|
if commit:
|
||||||
|
instance.save()
|
||||||
|
for f in opts.many_to_many:
|
||||||
|
setattr(instance, f.attname, clean_data[f.name])
|
||||||
|
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
|
||||||
|
# data will be lost. This happens because a many-to-many options cannot be
|
||||||
|
# set on an object until after it's saved. Maybe we should raise an
|
||||||
|
# exception in that case.
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def make_instance_save(instance):
|
||||||
|
"Returns the save() method for a form_for_instance Form."
|
||||||
|
def save(self, commit=True):
|
||||||
|
return save_instance(self, instance, commit)
|
||||||
|
return save
|
||||||
|
|
||||||
|
def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield()):
|
||||||
|
"""
|
||||||
|
Returns a Form class for the given Django model class.
|
||||||
|
|
||||||
|
Provide ``form`` if you want to use a custom BaseForm subclass.
|
||||||
|
|
||||||
|
Provide ``formfield_callback`` if you want to define different logic for
|
||||||
|
determining the formfield for a given database field. It's a callable that
|
||||||
|
takes a database Field instance and returns a form Field instance.
|
||||||
|
"""
|
||||||
|
opts = model._meta
|
||||||
|
field_list = []
|
||||||
|
for f in opts.fields + opts.many_to_many:
|
||||||
|
formfield = formfield_callback(f)
|
||||||
|
if formfield:
|
||||||
|
field_list.append((f.name, formfield))
|
||||||
|
fields = SortedDictFromList(field_list)
|
||||||
|
return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save})
|
||||||
|
|
||||||
|
def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
|
||||||
|
"""
|
||||||
|
Returns a Form class for the given Django model instance.
|
||||||
|
|
||||||
|
Provide ``form`` if you want to use a custom BaseForm subclass.
|
||||||
|
|
||||||
|
Provide ``formfield_callback`` if you want to define different logic for
|
||||||
|
determining the formfield for a given database field. It's a callable that
|
||||||
|
takes a database Field instance, plus **kwargs, and returns a form Field
|
||||||
|
instance with the given kwargs (i.e. 'initial').
|
||||||
|
"""
|
||||||
|
model = instance.__class__
|
||||||
|
opts = model._meta
|
||||||
|
field_list = []
|
||||||
|
for f in opts.fields + opts.many_to_many:
|
||||||
|
current_value = f.value_from_object(instance)
|
||||||
|
formfield = formfield_callback(f, initial=current_value)
|
||||||
|
if formfield:
|
||||||
|
field_list.append((f.name, formfield))
|
||||||
|
fields = SortedDictFromList(field_list)
|
||||||
|
return type(opts.object_name + 'InstanceForm', (form,),
|
||||||
|
{'base_fields': fields, '_model': model, 'save': make_instance_save(instance)})
|
||||||
|
|
||||||
def form_for_fields(field_list):
|
def form_for_fields(field_list):
|
||||||
"Returns a Form instance for the given list of Django database field instances."
|
"Returns a Form class for the given list of Django database field instances."
|
||||||
raise NotImplementedError
|
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
|
||||||
|
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.html import escape
|
||||||
|
|
||||||
|
# Converts a dictionary to a single string with key="value", XML-style with
|
||||||
|
# a leading space. Assumes keys do not need to be XML-escaped.
|
||||||
|
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
||||||
|
|
||||||
def smart_unicode(s):
|
def smart_unicode(s):
|
||||||
if not isinstance(s, basestring):
|
if not isinstance(s, basestring):
|
||||||
|
@ -3,13 +3,16 @@ HTML Widget classes
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
|
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',
|
||||||
'Textarea', 'CheckboxInput',
|
'FileInput', 'Textarea', 'CheckboxInput',
|
||||||
'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
|
'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
|
||||||
|
'MultiWidget', 'SplitDateTimeWidget',
|
||||||
)
|
)
|
||||||
|
|
||||||
from util import StrAndUnicode, smart_unicode
|
from util import flatatt, StrAndUnicode, smart_unicode
|
||||||
|
from django.utils.datastructures import MultiValueDict
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
from django.utils.translation import gettext
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -17,18 +20,19 @@ try:
|
|||||||
except NameError:
|
except NameError:
|
||||||
from sets import Set as set # Python 2.3 fallback
|
from sets import Set as set # Python 2.3 fallback
|
||||||
|
|
||||||
# Converts a dictionary to a single string with key="value", XML-style with
|
|
||||||
# a leading space. Assumes keys do not need to be XML-escaped.
|
|
||||||
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
|
||||||
|
|
||||||
class Widget(object):
|
class Widget(object):
|
||||||
requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
|
|
||||||
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
||||||
|
|
||||||
def __init__(self, attrs=None):
|
def __init__(self, attrs=None):
|
||||||
self.attrs = attrs or {}
|
self.attrs = attrs or {}
|
||||||
|
|
||||||
def render(self, name, value):
|
def render(self, name, value, attrs=None):
|
||||||
|
"""
|
||||||
|
Returns this Widget rendered as HTML, as a Unicode string.
|
||||||
|
|
||||||
|
The 'value' given is not guaranteed to be valid input, so subclass
|
||||||
|
implementations should program defensively.
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||||
@ -64,6 +68,7 @@ class Input(Widget):
|
|||||||
type='radio', which are special).
|
type='radio', which are special).
|
||||||
"""
|
"""
|
||||||
input_type = None # Subclasses must define this.
|
input_type = None # Subclasses must define this.
|
||||||
|
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
||||||
@ -80,6 +85,26 @@ class HiddenInput(Input):
|
|||||||
input_type = 'hidden'
|
input_type = 'hidden'
|
||||||
is_hidden = True
|
is_hidden = True
|
||||||
|
|
||||||
|
class MultipleHiddenInput(HiddenInput):
|
||||||
|
"""
|
||||||
|
A widget that handles <input type="hidden"> for fields that have a list
|
||||||
|
of values.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None, choices=()):
|
||||||
|
# choices can be any iterable
|
||||||
|
self.attrs = attrs or {}
|
||||||
|
self.choices = choices
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
|
if value is None: value = []
|
||||||
|
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
||||||
|
return u'\n'.join([(u'<input%s />' % flatatt(dict(value=smart_unicode(v), **final_attrs))) for v in value])
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, name):
|
||||||
|
if isinstance(data, MultiValueDict):
|
||||||
|
return data.getlist(name)
|
||||||
|
return data.get(name, None)
|
||||||
|
|
||||||
class FileInput(Input):
|
class FileInput(Input):
|
||||||
input_type = 'file'
|
input_type = 'file'
|
||||||
|
|
||||||
@ -111,9 +136,11 @@ class CheckboxInput(Widget):
|
|||||||
|
|
||||||
class Select(Widget):
|
class Select(Widget):
|
||||||
def __init__(self, attrs=None, choices=()):
|
def __init__(self, attrs=None, choices=()):
|
||||||
# choices can be any iterable
|
|
||||||
self.attrs = attrs or {}
|
self.attrs = attrs or {}
|
||||||
self.choices = choices
|
# choices can be any iterable, but we may need to render this widget
|
||||||
|
# multiple times. Thus, collapse it into a list so it can be consumed
|
||||||
|
# more than once.
|
||||||
|
self.choices = list(choices)
|
||||||
|
|
||||||
def render(self, name, value, attrs=None, choices=()):
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
@ -127,8 +154,26 @@ class Select(Widget):
|
|||||||
output.append(u'</select>')
|
output.append(u'</select>')
|
||||||
return u'\n'.join(output)
|
return u'\n'.join(output)
|
||||||
|
|
||||||
|
class NullBooleanSelect(Select):
|
||||||
|
"""
|
||||||
|
A Select Widget intended to be used with NullBooleanField.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
choices = ((u'1', gettext('Unknown')), (u'2', gettext('Yes')), (u'3', gettext('No')))
|
||||||
|
super(NullBooleanSelect, self).__init__(attrs, choices)
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
|
try:
|
||||||
|
value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value]
|
||||||
|
except KeyError:
|
||||||
|
value = u'1'
|
||||||
|
return super(NullBooleanSelect, self).render(name, value, attrs, choices)
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, name):
|
||||||
|
value = data.get(name, None)
|
||||||
|
return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
|
||||||
|
|
||||||
class SelectMultiple(Widget):
|
class SelectMultiple(Widget):
|
||||||
requires_data_list = True
|
|
||||||
def __init__(self, attrs=None, choices=()):
|
def __init__(self, attrs=None, choices=()):
|
||||||
# choices can be any iterable
|
# choices can be any iterable
|
||||||
self.attrs = attrs or {}
|
self.attrs = attrs or {}
|
||||||
@ -146,19 +191,25 @@ class SelectMultiple(Widget):
|
|||||||
output.append(u'</select>')
|
output.append(u'</select>')
|
||||||
return u'\n'.join(output)
|
return u'\n'.join(output)
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, name):
|
||||||
|
if isinstance(data, MultiValueDict):
|
||||||
|
return data.getlist(name)
|
||||||
|
return data.get(name, None)
|
||||||
|
|
||||||
class RadioInput(StrAndUnicode):
|
class RadioInput(StrAndUnicode):
|
||||||
"An object used by RadioFieldRenderer that represents a single <input type='radio'>."
|
"An object used by RadioFieldRenderer that represents a single <input type='radio'>."
|
||||||
def __init__(self, name, value, attrs, choice, index):
|
def __init__(self, name, value, attrs, choice, index):
|
||||||
self.name, self.value = name, value
|
self.name, self.value = name, value
|
||||||
self.attrs = attrs
|
self.attrs = attrs
|
||||||
self.choice_value, self.choice_label = choice
|
self.choice_value = smart_unicode(choice[0])
|
||||||
|
self.choice_label = smart_unicode(choice[1])
|
||||||
self.index = index
|
self.index = index
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
||||||
|
|
||||||
def is_checked(self):
|
def is_checked(self):
|
||||||
return self.value == smart_unicode(self.choice_value)
|
return self.value == self.choice_value
|
||||||
|
|
||||||
def tag(self):
|
def tag(self):
|
||||||
if self.attrs.has_key('id'):
|
if self.attrs.has_key('id'):
|
||||||
@ -178,6 +229,10 @@ class RadioFieldRenderer(StrAndUnicode):
|
|||||||
for i, choice in enumerate(self.choices):
|
for i, choice in enumerate(self.choices):
|
||||||
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
|
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
|
||||||
|
|
||||||
|
def __getitem__(self, idx):
|
||||||
|
choice = self.choices[idx] # Let the IndexError propogate
|
||||||
|
return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
"Outputs a <ul> for this set of radio fields."
|
"Outputs a <ul> for this set of radio fields."
|
||||||
return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % w for w in self])
|
return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % w for w in self])
|
||||||
@ -203,11 +258,16 @@ class RadioSelect(Select):
|
|||||||
class CheckboxSelectMultiple(SelectMultiple):
|
class CheckboxSelectMultiple(SelectMultiple):
|
||||||
def render(self, name, value, attrs=None, choices=()):
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
if value is None: value = []
|
if value is None: value = []
|
||||||
|
has_id = attrs and attrs.has_key('id')
|
||||||
final_attrs = self.build_attrs(attrs, name=name)
|
final_attrs = self.build_attrs(attrs, name=name)
|
||||||
output = [u'<ul>']
|
output = [u'<ul>']
|
||||||
str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
|
str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
|
||||||
|
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)
|
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
|
||||||
for option_value, option_label in chain(self.choices, choices):
|
|
||||||
option_value = smart_unicode(option_value)
|
option_value = smart_unicode(option_value)
|
||||||
rendered_cb = cb.render(name, option_value)
|
rendered_cb = cb.render(name, option_value)
|
||||||
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label))))
|
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label))))
|
||||||
@ -220,3 +280,66 @@ class CheckboxSelectMultiple(SelectMultiple):
|
|||||||
id_ += '_0'
|
id_ += '_0'
|
||||||
return id_
|
return id_
|
||||||
id_for_label = classmethod(id_for_label)
|
id_for_label = classmethod(id_for_label)
|
||||||
|
|
||||||
|
class MultiWidget(Widget):
|
||||||
|
"""
|
||||||
|
A widget that is composed of multiple widgets.
|
||||||
|
|
||||||
|
Its render() method takes a "decompressed" list of values, not a single
|
||||||
|
value. Each value in this list is rendered in the corresponding widget --
|
||||||
|
the first value is rendered in the first widget, the second value is
|
||||||
|
rendered in the second widget, etc.
|
||||||
|
|
||||||
|
Subclasses should implement decompress(), which specifies how a single
|
||||||
|
value should be converted to a list of values. Subclasses should not
|
||||||
|
have to implement clean().
|
||||||
|
|
||||||
|
Subclasses may implement format_output(), which takes the list of rendered
|
||||||
|
widgets and returns HTML that formats them any way you'd like.
|
||||||
|
|
||||||
|
You'll probably want to use this with MultiValueField.
|
||||||
|
"""
|
||||||
|
def __init__(self, widgets, attrs=None):
|
||||||
|
self.widgets = [isinstance(w, type) and w() or w for w in widgets]
|
||||||
|
super(MultiWidget, self).__init__(attrs)
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None):
|
||||||
|
# value is a list of values, each corresponding to a widget
|
||||||
|
# in self.widgets.
|
||||||
|
if not isinstance(value, list):
|
||||||
|
value = self.decompress(value)
|
||||||
|
output = []
|
||||||
|
for i, widget in enumerate(self.widgets):
|
||||||
|
try:
|
||||||
|
widget_value = value[i]
|
||||||
|
except KeyError:
|
||||||
|
widget_value = None
|
||||||
|
output.append(widget.render(name + '_%s' % i, widget_value, attrs))
|
||||||
|
return self.format_output(output)
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, name):
|
||||||
|
return [data.get(name + '_%s' % i) for i in range(len(self.widgets))]
|
||||||
|
|
||||||
|
def format_output(self, rendered_widgets):
|
||||||
|
return u''.join(rendered_widgets)
|
||||||
|
|
||||||
|
def decompress(self, value):
|
||||||
|
"""
|
||||||
|
Returns a list of decompressed values for the given compressed value.
|
||||||
|
The given value can be assumed to be valid, but not necessarily
|
||||||
|
non-empty.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError('Subclasses must implement this method.')
|
||||||
|
|
||||||
|
class SplitDateTimeWidget(MultiWidget):
|
||||||
|
"""
|
||||||
|
A Widget that splits datetime input into two <input type="text"> boxes.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs))
|
||||||
|
super(SplitDateTimeWidget, self).__init__(widgets, attrs)
|
||||||
|
|
||||||
|
def decompress(self, value):
|
||||||
|
if value:
|
||||||
|
return [value.date(), value.time()]
|
||||||
|
return [None, None]
|
||||||
|
1010
django/oldforms/__init__.py
Normal file
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.template import loader
|
||||||
from django.http import HttpResponse, Http404
|
from django.http import HttpResponse, Http404
|
||||||
|
from django.db.models.manager import Manager
|
||||||
|
|
||||||
def render_to_response(*args, **kwargs):
|
def render_to_response(*args, **kwargs):
|
||||||
return HttpResponse(loader.render_to_string(*args, **kwargs))
|
return HttpResponse(loader.render_to_string(*args, **kwargs))
|
||||||
load_and_render = render_to_response # For backwards compatibility.
|
load_and_render = render_to_response # For backwards compatibility.
|
||||||
|
|
||||||
def get_object_or_404(klass, *args, **kwargs):
|
def get_object_or_404(klass, *args, **kwargs):
|
||||||
|
if isinstance(klass, Manager):
|
||||||
|
manager = klass
|
||||||
|
klass = manager.model
|
||||||
|
else:
|
||||||
|
manager = klass._default_manager
|
||||||
try:
|
try:
|
||||||
return klass._default_manager.get(*args, **kwargs)
|
return manager.get(*args, **kwargs)
|
||||||
except klass.DoesNotExist:
|
except klass.DoesNotExist:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
def get_list_or_404(klass, *args, **kwargs):
|
def get_list_or_404(klass, *args, **kwargs):
|
||||||
obj_list = list(klass._default_manager.filter(*args, **kwargs))
|
if isinstance(klass, Manager):
|
||||||
|
manager = klass
|
||||||
|
else:
|
||||||
|
manager = klass._default_manager
|
||||||
|
obj_list = list(manager.filter(*args, **kwargs))
|
||||||
if not obj_list:
|
if not obj_list:
|
||||||
raise Http404
|
raise Http404
|
||||||
return obj_list
|
return obj_list
|
||||||
|
@ -27,20 +27,38 @@ def fix_ampersands(value):
|
|||||||
from django.utils.html import fix_ampersands
|
from django.utils.html import fix_ampersands
|
||||||
return fix_ampersands(value)
|
return fix_ampersands(value)
|
||||||
|
|
||||||
def floatformat(text):
|
def floatformat(text, arg=-1):
|
||||||
"""
|
"""
|
||||||
Displays a floating point number as 34.2 (with one decimal place) -- but
|
If called without an argument, displays a floating point
|
||||||
only if there's a point to be displayed
|
number as 34.2 -- but only if there's a point to be displayed.
|
||||||
|
With a positive numeric argument, it displays that many decimal places
|
||||||
|
always.
|
||||||
|
With a negative numeric argument, it will display that many decimal
|
||||||
|
places -- but only if there's places to be displayed.
|
||||||
|
Examples:
|
||||||
|
num1 = 34.23234
|
||||||
|
num2 = 34.00000
|
||||||
|
num1|floatformat results in 34.2
|
||||||
|
num2|floatformat is 34
|
||||||
|
num1|floatformat:3 is 34.232
|
||||||
|
num2|floatformat:3 is 34.000
|
||||||
|
num1|floatformat:-3 is 34.232
|
||||||
|
num2|floatformat:-3 is 34
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
f = float(text)
|
f = float(text)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return ''
|
return ''
|
||||||
|
try:
|
||||||
|
d = int(arg)
|
||||||
|
except ValueError:
|
||||||
|
return str(f)
|
||||||
m = f - int(f)
|
m = f - int(f)
|
||||||
if m:
|
if not m and d < 0:
|
||||||
return '%.1f' % f
|
|
||||||
else:
|
|
||||||
return '%d' % int(f)
|
return '%d' % int(f)
|
||||||
|
else:
|
||||||
|
formatstr = '%%.%df' % abs(d)
|
||||||
|
return formatstr % f
|
||||||
|
|
||||||
def linenumbers(value):
|
def linenumbers(value):
|
||||||
"Displays text with line numbers"
|
"Displays text with line numbers"
|
||||||
|
@ -81,6 +81,10 @@ class SortedDict(dict):
|
|||||||
self.keyOrder.append(key)
|
self.keyOrder.append(key)
|
||||||
return dict.setdefault(self, key, default)
|
return dict.setdefault(self, key, default)
|
||||||
|
|
||||||
|
def value_for_index(self, index):
|
||||||
|
"Returns the value of the item at the given zero-based index."
|
||||||
|
return self[self.keyOrder[index]]
|
||||||
|
|
||||||
class MultiValueDictKeyError(KeyError):
|
class MultiValueDictKeyError(KeyError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ Usage:
|
|||||||
>>>
|
>>>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.dates import MONTHS, MONTHS_AP, WEEKDAYS
|
from django.utils.dates import MONTHS, MONTHS_3, MONTHS_AP, WEEKDAYS
|
||||||
from django.utils.tzinfo import LocalTimezone
|
from django.utils.tzinfo import LocalTimezone
|
||||||
from calendar import isleap, monthrange
|
from calendar import isleap, monthrange
|
||||||
import re, time
|
import re, time
|
||||||
@ -147,7 +147,7 @@ class DateFormat(TimeFormat):
|
|||||||
|
|
||||||
def M(self):
|
def M(self):
|
||||||
"Month, textual, 3 letters; e.g. 'Jan'"
|
"Month, textual, 3 letters; e.g. 'Jan'"
|
||||||
return MONTHS[self.data.month][0:3]
|
return MONTHS_3[self.data.month].title()
|
||||||
|
|
||||||
def n(self):
|
def n(self):
|
||||||
"Month without leading zeros; i.e. '1' to '12'"
|
"Month without leading zeros; i.e. '1' to '12'"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
simplejson 1.3
|
simplejson 1.5
|
||||||
Copyright (c) 2006 Bob Ippolito
|
Copyright (c) 2006 Bob Ippolito
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
@ -27,6 +27,21 @@ Encoding basic Python object hierarchies::
|
|||||||
>>> io.getvalue()
|
>>> io.getvalue()
|
||||||
'["streaming API"]'
|
'["streaming API"]'
|
||||||
|
|
||||||
|
Compact encoding::
|
||||||
|
|
||||||
|
>>> import simplejson
|
||||||
|
>>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
|
||||||
|
'[1,2,3,{"4":5,"6":7}]'
|
||||||
|
|
||||||
|
Pretty printing::
|
||||||
|
|
||||||
|
>>> import simplejson
|
||||||
|
>>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
|
||||||
|
{
|
||||||
|
"4": 5,
|
||||||
|
"6": 7
|
||||||
|
}
|
||||||
|
|
||||||
Decoding JSON::
|
Decoding JSON::
|
||||||
|
|
||||||
>>> import simplejson
|
>>> import simplejson
|
||||||
@ -68,10 +83,10 @@ Extending JSONEncoder::
|
|||||||
['[', '2.0', ', ', '1.0', ']']
|
['[', '2.0', ', ', '1.0', ']']
|
||||||
|
|
||||||
|
|
||||||
Note that the JSON produced by this module is a subset of YAML,
|
Note that the JSON produced by this module's default settings
|
||||||
so it may be used as a serializer for that as well.
|
is a subset of YAML, so it may be used as a serializer for that as well.
|
||||||
"""
|
"""
|
||||||
__version__ = '1.3'
|
__version__ = '1.5'
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'dump', 'dumps', 'load', 'loads',
|
'dump', 'dumps', 'load', 'loads',
|
||||||
'JSONDecoder', 'JSONEncoder',
|
'JSONDecoder', 'JSONEncoder',
|
||||||
@ -81,7 +96,7 @@ from django.utils.simplejson.decoder import JSONDecoder
|
|||||||
from django.utils.simplejson.encoder import JSONEncoder
|
from django.utils.simplejson.encoder import JSONEncoder
|
||||||
|
|
||||||
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||||
allow_nan=True, cls=None, **kw):
|
allow_nan=True, cls=None, indent=None, **kw):
|
||||||
"""
|
"""
|
||||||
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
|
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
|
||||||
``.write()``-supporting file-like object).
|
``.write()``-supporting file-like object).
|
||||||
@ -105,6 +120,10 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
|||||||
in strict compliance of the JSON specification, instead of using the
|
in strict compliance of the JSON specification, instead of using the
|
||||||
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
||||||
|
|
||||||
|
If ``indent`` is a non-negative integer, then JSON array elements and object
|
||||||
|
members will be pretty-printed with that indent level. An indent level
|
||||||
|
of 0 will only insert newlines. ``None`` is the most compact representation.
|
||||||
|
|
||||||
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
||||||
``.default()`` method to serialize additional types), specify it with
|
``.default()`` method to serialize additional types), specify it with
|
||||||
the ``cls`` kwarg.
|
the ``cls`` kwarg.
|
||||||
@ -112,7 +131,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
|||||||
if cls is None:
|
if cls is None:
|
||||||
cls = JSONEncoder
|
cls = JSONEncoder
|
||||||
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||||
check_circular=check_circular, allow_nan=allow_nan,
|
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
|
||||||
**kw).iterencode(obj)
|
**kw).iterencode(obj)
|
||||||
# could accelerate with writelines in some versions of Python, at
|
# could accelerate with writelines in some versions of Python, at
|
||||||
# a debuggability cost
|
# a debuggability cost
|
||||||
@ -120,7 +139,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
|||||||
fp.write(chunk)
|
fp.write(chunk)
|
||||||
|
|
||||||
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||||
allow_nan=True, cls=None, **kw):
|
allow_nan=True, cls=None, indent=None, separators=None, **kw):
|
||||||
"""
|
"""
|
||||||
Serialize ``obj`` to a JSON formatted ``str``.
|
Serialize ``obj`` to a JSON formatted ``str``.
|
||||||
|
|
||||||
@ -141,14 +160,26 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
|||||||
strict compliance of the JSON specification, instead of using the
|
strict compliance of the JSON specification, instead of using the
|
||||||
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
||||||
|
|
||||||
|
If ``indent`` is a non-negative integer, then JSON array elements and
|
||||||
|
object members will be pretty-printed with that indent level. An indent
|
||||||
|
level of 0 will only insert newlines. ``None`` is the most compact
|
||||||
|
representation.
|
||||||
|
|
||||||
|
If ``separators`` is an ``(item_separator, dict_separator)`` tuple
|
||||||
|
then it will be used instead of the default ``(', ', ': ')`` separators.
|
||||||
|
``(',', ':')`` is the most compact JSON representation.
|
||||||
|
|
||||||
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
||||||
``.default()`` method to serialize additional types), specify it with
|
``.default()`` method to serialize additional types), specify it with
|
||||||
the ``cls`` kwarg.
|
the ``cls`` kwarg.
|
||||||
"""
|
"""
|
||||||
if cls is None:
|
if cls is None:
|
||||||
cls = JSONEncoder
|
cls = JSONEncoder
|
||||||
return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
return cls(
|
||||||
check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj)
|
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||||
|
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
|
||||||
|
separators=separators,
|
||||||
|
**kw).encode(obj)
|
||||||
|
|
||||||
def load(fp, encoding=None, cls=None, object_hook=None, **kw):
|
def load(fp, encoding=None, cls=None, object_hook=None, **kw):
|
||||||
"""
|
"""
|
||||||
|
@ -127,6 +127,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
|
|||||||
raise ValueError(errmsg("Expecting property name", s, end))
|
raise ValueError(errmsg("Expecting property name", s, end))
|
||||||
end += 1
|
end += 1
|
||||||
encoding = getattr(context, 'encoding', None)
|
encoding = getattr(context, 'encoding', None)
|
||||||
|
iterscan = JSONScanner.iterscan
|
||||||
while True:
|
while True:
|
||||||
key, end = scanstring(s, end, encoding)
|
key, end = scanstring(s, end, encoding)
|
||||||
end = _w(s, end).end()
|
end = _w(s, end).end()
|
||||||
@ -134,7 +135,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
|
|||||||
raise ValueError(errmsg("Expecting : delimiter", s, end))
|
raise ValueError(errmsg("Expecting : delimiter", s, end))
|
||||||
end = _w(s, end + 1).end()
|
end = _w(s, end + 1).end()
|
||||||
try:
|
try:
|
||||||
value, end = JSONScanner.iterscan(s, idx=end).next()
|
value, end = iterscan(s, idx=end, context=context).next()
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise ValueError(errmsg("Expecting object", s, end))
|
raise ValueError(errmsg("Expecting object", s, end))
|
||||||
pairs[key] = value
|
pairs[key] = value
|
||||||
@ -164,9 +165,10 @@ def JSONArray(match, context, _w=WHITESPACE.match):
|
|||||||
nextchar = s[end:end + 1]
|
nextchar = s[end:end + 1]
|
||||||
if nextchar == ']':
|
if nextchar == ']':
|
||||||
return values, end + 1
|
return values, end + 1
|
||||||
|
iterscan = JSONScanner.iterscan
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
value, end = JSONScanner.iterscan(s, idx=end).next()
|
value, end = iterscan(s, idx=end, context=context).next()
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise ValueError(errmsg("Expecting object", s, end))
|
raise ValueError(errmsg("Expecting object", s, end))
|
||||||
values.append(value)
|
values.append(value)
|
||||||
|
@ -3,11 +3,11 @@ Implementation of JSONEncoder
|
|||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# this should match any kind of infinity
|
|
||||||
INFCHARS = re.compile(r'[infINF]')
|
|
||||||
ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
|
ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
|
||||||
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
|
ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
|
||||||
ESCAPE_DCT = {
|
ESCAPE_DCT = {
|
||||||
|
# escape all forward slashes to prevent </script> attack
|
||||||
|
'/': '\\/',
|
||||||
'\\': '\\\\',
|
'\\': '\\\\',
|
||||||
'"': '\\"',
|
'"': '\\"',
|
||||||
'\b': '\\b',
|
'\b': '\\b',
|
||||||
@ -16,31 +16,31 @@ ESCAPE_DCT = {
|
|||||||
'\r': '\\r',
|
'\r': '\\r',
|
||||||
'\t': '\\t',
|
'\t': '\\t',
|
||||||
}
|
}
|
||||||
for i in range(20):
|
for i in range(0x20):
|
||||||
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
|
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
|
||||||
|
|
||||||
|
# assume this produces an infinity on all machines (probably not guaranteed)
|
||||||
|
INFINITY = float('1e66666')
|
||||||
|
|
||||||
def floatstr(o, allow_nan=True):
|
def floatstr(o, allow_nan=True):
|
||||||
s = str(o)
|
# Check for specials. Note that this type of test is processor- and/or
|
||||||
# If the first non-sign is a digit then it's not a special value
|
# platform-specific, so do tests which don't depend on the internals.
|
||||||
if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
|
|
||||||
return s
|
if o != o:
|
||||||
elif not allow_nan:
|
text = 'NaN'
|
||||||
|
elif o == INFINITY:
|
||||||
|
text = 'Infinity'
|
||||||
|
elif o == -INFINITY:
|
||||||
|
text = '-Infinity'
|
||||||
|
else:
|
||||||
|
return str(o)
|
||||||
|
|
||||||
|
if not allow_nan:
|
||||||
raise ValueError("Out of range float values are not JSON compliant: %r"
|
raise ValueError("Out of range float values are not JSON compliant: %r"
|
||||||
% (o,))
|
% (o,))
|
||||||
# These are the string representations on the platforms I've tried
|
|
||||||
if s == 'nan':
|
return text
|
||||||
return 'NaN'
|
|
||||||
if s == 'inf':
|
|
||||||
return 'Infinity'
|
|
||||||
if s == '-inf':
|
|
||||||
return '-Infinity'
|
|
||||||
# NaN should either be inequal to itself, or equal to everything
|
|
||||||
if o != o or o == 0.0:
|
|
||||||
return 'NaN'
|
|
||||||
# Last ditch effort, assume inf
|
|
||||||
if o < 0:
|
|
||||||
return '-Infinity'
|
|
||||||
return 'Infinity'
|
|
||||||
|
|
||||||
def encode_basestring(s):
|
def encode_basestring(s):
|
||||||
"""
|
"""
|
||||||
@ -90,8 +90,11 @@ class JSONEncoder(object):
|
|||||||
implementation (to raise ``TypeError``).
|
implementation (to raise ``TypeError``).
|
||||||
"""
|
"""
|
||||||
__all__ = ['__init__', 'default', 'encode', 'iterencode']
|
__all__ = ['__init__', 'default', 'encode', 'iterencode']
|
||||||
|
item_separator = ', '
|
||||||
|
key_separator = ': '
|
||||||
def __init__(self, skipkeys=False, ensure_ascii=True,
|
def __init__(self, skipkeys=False, ensure_ascii=True,
|
||||||
check_circular=True, allow_nan=True, sort_keys=False):
|
check_circular=True, allow_nan=True, sort_keys=False,
|
||||||
|
indent=None, separators=None):
|
||||||
"""
|
"""
|
||||||
Constructor for JSONEncoder, with sensible defaults.
|
Constructor for JSONEncoder, with sensible defaults.
|
||||||
|
|
||||||
@ -116,6 +119,15 @@ class JSONEncoder(object):
|
|||||||
If sort_keys is True, then the output of dictionaries will be
|
If sort_keys is True, then the output of dictionaries will be
|
||||||
sorted by key; this is useful for regression tests to ensure
|
sorted by key; this is useful for regression tests to ensure
|
||||||
that JSON serializations can be compared on a day-to-day basis.
|
that JSON serializations can be compared on a day-to-day basis.
|
||||||
|
|
||||||
|
If indent is a non-negative integer, then JSON array
|
||||||
|
elements and object members will be pretty-printed with that
|
||||||
|
indent level. An indent level of 0 will only insert newlines.
|
||||||
|
None is the most compact representation.
|
||||||
|
|
||||||
|
If specified, separators should be a (item_separator, key_separator)
|
||||||
|
tuple. The default is (', ', ': '). To get the most compact JSON
|
||||||
|
representation you should specify (',', ':') to eliminate whitespace.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.skipkeys = skipkeys
|
self.skipkeys = skipkeys
|
||||||
@ -123,6 +135,13 @@ class JSONEncoder(object):
|
|||||||
self.check_circular = check_circular
|
self.check_circular = check_circular
|
||||||
self.allow_nan = allow_nan
|
self.allow_nan = allow_nan
|
||||||
self.sort_keys = sort_keys
|
self.sort_keys = sort_keys
|
||||||
|
self.indent = indent
|
||||||
|
self.current_indent_level = 0
|
||||||
|
if separators is not None:
|
||||||
|
self.item_separator, self.key_separator = separators
|
||||||
|
|
||||||
|
def _newline_indent(self):
|
||||||
|
return '\n' + (' ' * (self.indent * self.current_indent_level))
|
||||||
|
|
||||||
def _iterencode_list(self, lst, markers=None):
|
def _iterencode_list(self, lst, markers=None):
|
||||||
if not lst:
|
if not lst:
|
||||||
@ -134,14 +153,25 @@ class JSONEncoder(object):
|
|||||||
raise ValueError("Circular reference detected")
|
raise ValueError("Circular reference detected")
|
||||||
markers[markerid] = lst
|
markers[markerid] = lst
|
||||||
yield '['
|
yield '['
|
||||||
|
if self.indent is not None:
|
||||||
|
self.current_indent_level += 1
|
||||||
|
newline_indent = self._newline_indent()
|
||||||
|
separator = self.item_separator + newline_indent
|
||||||
|
yield newline_indent
|
||||||
|
else:
|
||||||
|
newline_indent = None
|
||||||
|
separator = self.item_separator
|
||||||
first = True
|
first = True
|
||||||
for value in lst:
|
for value in lst:
|
||||||
if first:
|
if first:
|
||||||
first = False
|
first = False
|
||||||
else:
|
else:
|
||||||
yield ', '
|
yield separator
|
||||||
for chunk in self._iterencode(value, markers):
|
for chunk in self._iterencode(value, markers):
|
||||||
yield chunk
|
yield chunk
|
||||||
|
if newline_indent is not None:
|
||||||
|
self.current_indent_level -= 1
|
||||||
|
yield self._newline_indent()
|
||||||
yield ']'
|
yield ']'
|
||||||
if markers is not None:
|
if markers is not None:
|
||||||
del markers[markerid]
|
del markers[markerid]
|
||||||
@ -156,6 +186,15 @@ class JSONEncoder(object):
|
|||||||
raise ValueError("Circular reference detected")
|
raise ValueError("Circular reference detected")
|
||||||
markers[markerid] = dct
|
markers[markerid] = dct
|
||||||
yield '{'
|
yield '{'
|
||||||
|
key_separator = self.key_separator
|
||||||
|
if self.indent is not None:
|
||||||
|
self.current_indent_level += 1
|
||||||
|
newline_indent = self._newline_indent()
|
||||||
|
item_separator = self.item_separator + newline_indent
|
||||||
|
yield newline_indent
|
||||||
|
else:
|
||||||
|
newline_indent = None
|
||||||
|
item_separator = self.item_separator
|
||||||
first = True
|
first = True
|
||||||
if self.ensure_ascii:
|
if self.ensure_ascii:
|
||||||
encoder = encode_basestring_ascii
|
encoder = encode_basestring_ascii
|
||||||
@ -190,11 +229,14 @@ class JSONEncoder(object):
|
|||||||
if first:
|
if first:
|
||||||
first = False
|
first = False
|
||||||
else:
|
else:
|
||||||
yield ', '
|
yield item_separator
|
||||||
yield encoder(key)
|
yield encoder(key)
|
||||||
yield ': '
|
yield key_separator
|
||||||
for chunk in self._iterencode(value, markers):
|
for chunk in self._iterencode(value, markers):
|
||||||
yield chunk
|
yield chunk
|
||||||
|
if newline_indent is not None:
|
||||||
|
self.current_indent_level -= 1
|
||||||
|
yield self._newline_indent()
|
||||||
yield '}'
|
yield '}'
|
||||||
if markers is not None:
|
if markers is not None:
|
||||||
del markers[markerid]
|
del markers[markerid]
|
||||||
|
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
|
import sre_parse, sre_compile, sre_constants
|
||||||
from sre_constants import BRANCH, SUBPATTERN
|
from sre_constants import BRANCH, SUBPATTERN
|
||||||
|
from re import VERBOSE, MULTILINE, DOTALL
|
||||||
import re
|
import re
|
||||||
|
|
||||||
__all__ = ['Scanner', 'pattern']
|
__all__ = ['Scanner', 'pattern']
|
||||||
|
|
||||||
FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL)
|
FLAGS = (VERBOSE | MULTILINE | DOTALL)
|
||||||
class Scanner(object):
|
class Scanner(object):
|
||||||
def __init__(self, lexicon, flags=FLAGS):
|
def __init__(self, lexicon, flags=FLAGS):
|
||||||
self.actions = [None]
|
self.actions = [None]
|
||||||
|
@ -8,17 +8,28 @@ capfirst = lambda x: x and x[0].upper() + x[1:]
|
|||||||
def wrap(text, width):
|
def wrap(text, width):
|
||||||
"""
|
"""
|
||||||
A word-wrap function that preserves existing line breaks and most spaces in
|
A word-wrap function that preserves existing line breaks and most spaces in
|
||||||
the text. Expects that existing line breaks are posix newlines (\n).
|
the text. Expects that existing line breaks are posix newlines.
|
||||||
See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
|
|
||||||
"""
|
"""
|
||||||
return reduce(lambda line, word, width=width: '%s%s%s' %
|
def _generator():
|
||||||
(line,
|
it = iter(text.split(' '))
|
||||||
' \n'[(len(line[line.rfind('\n')+1:])
|
word = it.next()
|
||||||
+ len(word.split('\n',1)[0]
|
yield word
|
||||||
) >= width)],
|
pos = len(word) - word.rfind('\n') - 1
|
||||||
word),
|
for word in it:
|
||||||
text.split(' ')
|
if "\n" in word:
|
||||||
)
|
lines = word.splitlines()
|
||||||
|
else:
|
||||||
|
lines = (word,)
|
||||||
|
pos += len(lines[0]) + 1
|
||||||
|
if pos > width:
|
||||||
|
yield '\n'
|
||||||
|
pos = len(lines[-1])
|
||||||
|
else:
|
||||||
|
yield ' '
|
||||||
|
if len(lines) > 1:
|
||||||
|
pos = len(lines[-1])
|
||||||
|
yield word
|
||||||
|
return "".join(_generator())
|
||||||
|
|
||||||
def truncate_words(s, num):
|
def truncate_words(s, num):
|
||||||
"Truncates a string after a certain number of words."
|
"Truncates a string after a certain number of words."
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.core.xheaders import populate_xheaders
|
from django.core.xheaders import populate_xheaders
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django import forms
|
from django import oldforms
|
||||||
from django.db.models import FileField
|
from django.db.models import FileField
|
||||||
from django.contrib.auth.views import redirect_to_login
|
from django.contrib.auth.views import redirect_to_login
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
@ -56,7 +56,7 @@ def create_object(request, model, template_name=None,
|
|||||||
new_data = manipulator.flatten_data()
|
new_data = manipulator.flatten_data()
|
||||||
|
|
||||||
# Create the FormWrapper, template, context, response
|
# Create the FormWrapper, template, context, response
|
||||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||||
if not template_name:
|
if not template_name:
|
||||||
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||||
t = template_loader.get_template(template_name)
|
t = template_loader.get_template(template_name)
|
||||||
@ -128,7 +128,7 @@ def update_object(request, model, object_id=None, slug=None,
|
|||||||
# This makes sure the form acurate represents the fields of the place.
|
# This makes sure the form acurate represents the fields of the place.
|
||||||
new_data = manipulator.flatten_data()
|
new_data = manipulator.flatten_data()
|
||||||
|
|
||||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||||
if not template_name:
|
if not template_name:
|
||||||
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||||
t = template_loader.get_template(template_name)
|
t = template_loader.get_template(template_name)
|
||||||
|
@ -17,7 +17,7 @@ admin
|
|||||||
The automatic Django administrative interface. For more information, see
|
The automatic Django administrative interface. For more information, see
|
||||||
`Tutorial 2`_.
|
`Tutorial 2`_.
|
||||||
|
|
||||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
.. _Tutorial 2: ../tutorial2/
|
||||||
|
|
||||||
auth
|
auth
|
||||||
====
|
====
|
||||||
@ -26,7 +26,7 @@ Django's authentication framework.
|
|||||||
|
|
||||||
See the `authentication documentation`_.
|
See the `authentication documentation`_.
|
||||||
|
|
||||||
.. _authentication documentation: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication documentation: ../authentication/
|
||||||
|
|
||||||
comments
|
comments
|
||||||
========
|
========
|
||||||
@ -46,7 +46,7 @@ A middleware for preventing Cross Site Request Forgeries
|
|||||||
|
|
||||||
See the `csrf documentation`_.
|
See the `csrf documentation`_.
|
||||||
|
|
||||||
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/
|
.. _csrf documentation: ../csrf/
|
||||||
|
|
||||||
formtools
|
formtools
|
||||||
=========
|
=========
|
||||||
@ -137,7 +137,7 @@ A framework for managing simple "flat" HTML content in a database.
|
|||||||
|
|
||||||
See the `flatpages documentation`_.
|
See the `flatpages documentation`_.
|
||||||
|
|
||||||
.. _flatpages documentation: http://www.djangoproject.com/documentation/flatpages/
|
.. _flatpages documentation: ../flatpages/
|
||||||
|
|
||||||
markup
|
markup
|
||||||
======
|
======
|
||||||
@ -157,7 +157,7 @@ A framework for managing redirects.
|
|||||||
|
|
||||||
See the `redirects documentation`_.
|
See the `redirects documentation`_.
|
||||||
|
|
||||||
.. _redirects documentation: http://www.djangoproject.com/documentation/redirects/
|
.. _redirects documentation: ../redirects/
|
||||||
|
|
||||||
sites
|
sites
|
||||||
=====
|
=====
|
||||||
@ -168,7 +168,7 @@ one or more sites.
|
|||||||
|
|
||||||
See the `sites documentation`_.
|
See the `sites documentation`_.
|
||||||
|
|
||||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
.. _sites documentation: ../sites/
|
||||||
|
|
||||||
sitemaps
|
sitemaps
|
||||||
========
|
========
|
||||||
@ -177,7 +177,7 @@ A framework for generating Google sitemap XML files.
|
|||||||
|
|
||||||
See the `sitemaps documentation`_.
|
See the `sitemaps documentation`_.
|
||||||
|
|
||||||
.. _sitemaps documentation: http://www.djangoproject.com/documentation/sitemaps/
|
.. _sitemaps documentation: ../sitemaps/
|
||||||
|
|
||||||
syndication
|
syndication
|
||||||
===========
|
===========
|
||||||
@ -186,7 +186,7 @@ A framework for generating syndication feeds, in RSS and Atom, quite easily.
|
|||||||
|
|
||||||
See the `syndication documentation`_.
|
See the `syndication documentation`_.
|
||||||
|
|
||||||
.. _syndication documentation: http://www.djangoproject.com/documentation/syndication/
|
.. _syndication documentation: ../syndication/
|
||||||
|
|
||||||
Other add-ons
|
Other add-ons
|
||||||
=============
|
=============
|
||||||
|
@ -65,7 +65,7 @@ are equivalent::
|
|||||||
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
|
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
|
||||||
PythonOption DJANGO_SETTINGS_MODULE mysite.settings
|
PythonOption DJANGO_SETTINGS_MODULE mysite.settings
|
||||||
|
|
||||||
.. _authentication system: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication system: ../authentication/
|
||||||
.. _Subversion: http://subversion.tigris.org/
|
.. _Subversion: http://subversion.tigris.org/
|
||||||
.. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html
|
.. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html
|
||||||
.. _custom permissions: http://www.djangoproject.com/documentation/authentication/#custom-permissions
|
.. _custom permissions: ../authentication/#custom-permissions
|
||||||
|
@ -98,26 +98,26 @@ change:
|
|||||||
rewrite before Django 1.0. Even if the change isn't quite that drastic,
|
rewrite before Django 1.0. Even if the change isn't quite that drastic,
|
||||||
there will at least be moderate changes.
|
there will at least be moderate changes.
|
||||||
|
|
||||||
.. _caching: http://www.djangoproject.com/documentation/cache/
|
.. _caching: ../cache/
|
||||||
.. _custom template tags and libraries: http://www.djangoproject.com/documentation/templates_python/
|
.. _custom template tags and libraries: ../templates_python/
|
||||||
.. _database lookup: http://www.djangoproject.com/documentation/db_api/
|
.. _database lookup: ../db_api/
|
||||||
.. _django-admin utility: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin utility: ../django_admin/
|
||||||
.. _fastcgi integration: http://www.djangoproject.com/documentation/fastcgi/
|
.. _fastcgi integration: ../fastcgi/
|
||||||
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
|
.. _flatpages: ../flatpages/
|
||||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
.. _generic views: ../generic_views/
|
||||||
.. _internationalization: http://www.djangoproject.com/documentation/i18n/
|
.. _internationalization: ../i18n/
|
||||||
.. _legacy database integration: http://www.djangoproject.com/documentation/legacy_databases/
|
.. _legacy database integration: ../legacy_databases/
|
||||||
.. _model definition: http://www.djangoproject.com/documentation/model_api/
|
.. _model definition: ../model_api/
|
||||||
.. _mod_python integration: http://www.djangoproject.com/documentation/modpython/
|
.. _mod_python integration: ../modpython/
|
||||||
.. _redirects: http://www.djangoproject.com/documentation/redirects/
|
.. _redirects: ../redirects/
|
||||||
.. _request/response objects: http://www.djangoproject.com/documentation/request_response/
|
.. _request/response objects: ../request_response/
|
||||||
.. _sending email: http://www.djangoproject.com/documentation/email/
|
.. _sending email: ../email/
|
||||||
.. _sessions: http://www.djangoproject.com/documentation/sessions/
|
.. _sessions: ../sessions/
|
||||||
.. _settings: http://www.djangoproject.com/documentation/settings/
|
.. _settings: ../settings/
|
||||||
.. _syndication: http://www.djangoproject.com/documentation/syndication/
|
.. _syndication: ../syndication/
|
||||||
.. _template language: http://www.djangoproject.com/documentation/templates/
|
.. _template language: ../templates/
|
||||||
.. _transactions: http://www.djangoproject.com/documentation/transactions/
|
.. _transactions: ../transactions/
|
||||||
.. _url dispatch: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _url dispatch: ../url_dispatch/
|
||||||
.. _forms and validation: http://www.djangoproject.com/documentation/forms/
|
.. _forms and validation: ../forms/
|
||||||
.. _serialization: http://www.djangoproject.com/documentation/serialization/
|
.. _serialization: ../serialization/
|
||||||
.. _authentication: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication: ../authentication/
|
||||||
|
@ -144,8 +144,8 @@ custom methods:
|
|||||||
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
|
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
|
||||||
doesn't allow profiles.
|
doesn't allow profiles.
|
||||||
|
|
||||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
.. _Django model: ../model_api/
|
||||||
.. _DEFAULT_FROM_EMAIL: http://www.djangoproject.com/documentation/settings/#default-from-email
|
.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
|
||||||
|
|
||||||
Manager functions
|
Manager functions
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
@ -271,8 +271,8 @@ previous section). You can tell them apart with ``is_authenticated()``, like so:
|
|||||||
else:
|
else:
|
||||||
# Do something for anonymous users.
|
# Do something for anonymous users.
|
||||||
|
|
||||||
.. _request objects: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
.. _request objects: ../request_response/#httprequest-objects
|
||||||
.. _session documentation: http://www.djangoproject.com/documentation/sessions/
|
.. _session documentation: ../sessions/
|
||||||
|
|
||||||
How to log a user in
|
How to log a user in
|
||||||
--------------------
|
--------------------
|
||||||
@ -441,8 +441,8 @@ block::
|
|||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
.. _forms documentation: http://www.djangoproject.com/documentation/forms/
|
.. _forms documentation: ../forms/
|
||||||
.. _site framework docs: http://www.djangoproject.com/documentation/sites/
|
.. _site framework docs: ../sites/
|
||||||
|
|
||||||
Limiting access to logged-in users that pass a test
|
Limiting access to logged-in users that pass a test
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
@ -544,7 +544,7 @@ For example::
|
|||||||
def limited_object_detail(*args, **kwargs):
|
def limited_object_detail(*args, **kwargs):
|
||||||
return object_detail(*args, **kwargs)
|
return object_detail(*args, **kwargs)
|
||||||
|
|
||||||
.. _generic view: http://www.djangoproject.com/documentation/generic_views/
|
.. _generic view: ../generic_views/
|
||||||
|
|
||||||
Permissions
|
Permissions
|
||||||
===========
|
===========
|
||||||
@ -606,7 +606,7 @@ This example model creates three custom permissions::
|
|||||||
The only thing this does is create those extra permissions when you run
|
The only thing this does is create those extra permissions when you run
|
||||||
``syncdb``.
|
``syncdb``.
|
||||||
|
|
||||||
.. _model Meta attribute: http://www.djangoproject.com/documentation/model_api/#meta-options
|
.. _model Meta attribute: ../model_api/#meta-options
|
||||||
|
|
||||||
API reference
|
API reference
|
||||||
-------------
|
-------------
|
||||||
@ -645,7 +645,7 @@ The currently logged-in user and his/her permissions are made available in the
|
|||||||
setting contains ``"django.core.context_processors.auth"``, which is default.
|
setting contains ``"django.core.context_processors.auth"``, which is default.
|
||||||
For more, see the `RequestContext docs`_.
|
For more, see the `RequestContext docs`_.
|
||||||
|
|
||||||
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-requestcontext
|
.. _RequestContext docs: ../templates_python/#subclassing-context-requestcontext
|
||||||
|
|
||||||
Users
|
Users
|
||||||
-----
|
-----
|
||||||
@ -691,7 +691,7 @@ Thus, you can check permissions in template ``{% if %}`` statements::
|
|||||||
<p>You don't have permission to do anything in the foo app.</p>
|
<p>You don't have permission to do anything in the foo app.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
.. _template context: http://www.djangoproject.com/documentation/templates_python/
|
.. _template context: ../templates_python/
|
||||||
|
|
||||||
Groups
|
Groups
|
||||||
======
|
======
|
||||||
@ -756,7 +756,7 @@ scenes, so any messages will be deleted even if you don't display them.
|
|||||||
Finally, note that this messages framework only works with users in the user
|
Finally, note that this messages framework only works with users in the user
|
||||||
database. To send messages to anonymous users, use the `session framework`_.
|
database. To send messages to anonymous users, use the `session framework`_.
|
||||||
|
|
||||||
.. _session framework: http://www.djangoproject.com/documentation/sessions/
|
.. _session framework: ../sessions/
|
||||||
|
|
||||||
Other authentication sources
|
Other authentication sources
|
||||||
============================
|
============================
|
||||||
|
@ -250,7 +250,7 @@ Additionally, ``CacheMiddleware`` automatically sets a few headers in each
|
|||||||
|
|
||||||
See the `middleware documentation`_ for more on middleware.
|
See the `middleware documentation`_ for more on middleware.
|
||||||
|
|
||||||
.. _`middleware documentation`: http://www.djangoproject.com/documentation/middleware/
|
.. _`middleware documentation`: ../middleware/
|
||||||
|
|
||||||
The per-view cache
|
The per-view cache
|
||||||
==================
|
==================
|
||||||
|
@ -22,6 +22,9 @@ of the community, so there are many ways you can help Django's development:
|
|||||||
likely to be skeptical of large-scale suggestions without some code to
|
likely to be skeptical of large-scale suggestions without some code to
|
||||||
back it up.
|
back it up.
|
||||||
|
|
||||||
|
* Triage patches that have been submitted by other users. Please read
|
||||||
|
`Ticket triage`_ below, for details on the triage process.
|
||||||
|
|
||||||
That's all you need to know if you'd like to join the Django development
|
That's all you need to know if you'd like to join the Django development
|
||||||
community. The rest of this document describes the details of how our community
|
community. The rest of this document describes the details of how our community
|
||||||
works and how it handles bugs, mailing lists, and all the other minutiae of
|
works and how it handles bugs, mailing lists, and all the other minutiae of
|
||||||
@ -44,8 +47,10 @@ particular:
|
|||||||
|
|
||||||
* **Do** write complete, reproducible, specific bug reports. Include as
|
* **Do** write complete, reproducible, specific bug reports. Include as
|
||||||
much information as you possibly can, complete with code snippets, test
|
much information as you possibly can, complete with code snippets, test
|
||||||
cases, etc. A minimal example that illustrates the bug in a nice small
|
cases, etc. This means including a clear, concise description of the
|
||||||
test case is the best possible bug report.
|
problem, and a clear set of instructions for replicating the problem.
|
||||||
|
A minimal example that illustrates the bug in a nice small test case
|
||||||
|
is the best possible bug report.
|
||||||
|
|
||||||
* **Don't** use the ticket system to ask support questions. Use the
|
* **Don't** use the ticket system to ask support questions. Use the
|
||||||
`django-users`_ list, or the `#django`_ IRC channel for that.
|
`django-users`_ list, or the `#django`_ IRC channel for that.
|
||||||
@ -117,9 +122,99 @@ Patch style
|
|||||||
* Name the patch file with a ``.diff`` extension; this will let the ticket
|
* Name the patch file with a ``.diff`` extension; this will let the ticket
|
||||||
tracker apply correct syntax highlighting, which is quite helpful.
|
tracker apply correct syntax highlighting, which is quite helpful.
|
||||||
|
|
||||||
* Put the prefix "[patch] " before the title of your ticket. This will make
|
* Check the "Has patch" box on the ticket details. This will make it
|
||||||
it obvious that the ticket includes a patch, and it will add the ticket
|
obvious that the ticket includes a patch, and it will add the ticket to
|
||||||
to the `list of tickets with patches`_.
|
the `list of tickets with patches`_.
|
||||||
|
|
||||||
|
* The code required to fix a problem or add a feature is an essential part
|
||||||
|
of a patch, but it is not the only part. A good patch should also include
|
||||||
|
a regression test to validate the behavior that has been fixed (and prevent
|
||||||
|
the problem from arising again).
|
||||||
|
|
||||||
|
* If the code associated with a patch adds a new feature, or modifies behavior
|
||||||
|
of an existing feature, the patch should also contain documentation.
|
||||||
|
|
||||||
|
Non-trivial patches
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
A "non-trivial" patch is one that is more than a simple bug fix. It's a patch
|
||||||
|
that introduces Django functionality and makes some sort of design decision.
|
||||||
|
|
||||||
|
If you provide a non-trivial patch, include evidence that alternatives have
|
||||||
|
been discussed on `django-developers`_. If you're not sure whether your patch
|
||||||
|
should be considered non-trivial, just ask.
|
||||||
|
|
||||||
|
Ticket triage
|
||||||
|
=============
|
||||||
|
|
||||||
|
Unfortunately, not all bug reports in the `ticket tracker`_ provide all
|
||||||
|
the `required details`_. A number of tickets have patches, but those patches
|
||||||
|
don't meet all the requirements of a `good patch`_.
|
||||||
|
|
||||||
|
One way to help out is to *triage* bugs that have been reported by other
|
||||||
|
users. A couple of dedicated volunteers work on this regularly, but more help
|
||||||
|
is always appreciated.
|
||||||
|
|
||||||
|
Most of the workflow is based around the concept of a ticket's "triage stage".
|
||||||
|
This stage describes where in its lifetime a given ticket is at any time.
|
||||||
|
Along with a handful of flags, this field easily tells us what and who each
|
||||||
|
ticket is waiting on.
|
||||||
|
|
||||||
|
Since a picture is worth a thousand words, let's start there:
|
||||||
|
|
||||||
|
.. image:: http://media.djangoproject.com/img/doc/djangotickets.png
|
||||||
|
:height: 451
|
||||||
|
:width: 590
|
||||||
|
:alt: Django's ticket workflow
|
||||||
|
|
||||||
|
We've got two roles here:
|
||||||
|
|
||||||
|
* Core developers: people with commit access who make the decisions and
|
||||||
|
write the bulk of the code.
|
||||||
|
|
||||||
|
* Ticket triagers: community members who keep track of tickets, making
|
||||||
|
sure the tickets are always categorized correctly.
|
||||||
|
|
||||||
|
Second, note the four triage stages:
|
||||||
|
|
||||||
|
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
|
||||||
|
examine the ticket and move it along.
|
||||||
|
|
||||||
|
2. "Design decision needed" means "this concept requires a design
|
||||||
|
decision," which should be discussed either in the ticket comments or on
|
||||||
|
django-developers.
|
||||||
|
|
||||||
|
3. Once a ticket is ruled to be approved for fixing, it's moved into the
|
||||||
|
"Accepted" stage. This stage is where all the real work gets done.
|
||||||
|
|
||||||
|
4. If a ticket has an associated patch (see below), a triager will review the
|
||||||
|
patch. If the patch is complete, it'll be marked as "ready for checkin" so
|
||||||
|
that a core developer knows to review and check in the patches.
|
||||||
|
|
||||||
|
The second part of this workflow involves a set of flags the describe what the
|
||||||
|
ticket has or needs in order to be "ready for checkin":
|
||||||
|
|
||||||
|
"Has patch"
|
||||||
|
The means the ticket has an associated patch_. These will be
|
||||||
|
reviewed to see if the patch is "good".
|
||||||
|
|
||||||
|
"Needs documentation"
|
||||||
|
This flag is used for tickets with patches that need associated
|
||||||
|
documentation. Complete documentation of features is a prerequisite
|
||||||
|
before we can check a fix into the codebase.
|
||||||
|
|
||||||
|
"Needs tests"
|
||||||
|
This flags the patch as needing associated unit tests. Again, this is a
|
||||||
|
required part of a valid patch.
|
||||||
|
|
||||||
|
"Patch needs improvement"
|
||||||
|
This flag means that although the ticket *has* a patch, it's not quite
|
||||||
|
ready for checkin. This could mean the patch no longer applies
|
||||||
|
cleanly, or that the code doesn't live up to our standards.
|
||||||
|
|
||||||
|
.. _required details: `Reporting bugs`_
|
||||||
|
.. _good patch: `Patch style`_
|
||||||
|
.. _patch: `Submitting patches`_
|
||||||
|
|
||||||
Submitting and maintaining translations
|
Submitting and maintaining translations
|
||||||
=======================================
|
=======================================
|
||||||
@ -137,7 +232,7 @@ translated, here's what to do:
|
|||||||
`i18n documentation`_.
|
`i18n documentation`_.
|
||||||
|
|
||||||
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
|
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
|
||||||
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
|
.. _i18n documentation: ../i18n/
|
||||||
|
|
||||||
Coding style
|
Coding style
|
||||||
============
|
============
|
||||||
@ -262,7 +357,7 @@ The Django tests all use the testing infrastructure that ships with Django for
|
|||||||
testing applications. See `Testing Django applications`_ for an explanation of
|
testing applications. See `Testing Django applications`_ for an explanation of
|
||||||
how to write new tests.
|
how to write new tests.
|
||||||
|
|
||||||
.. _Testing Django applications: http://www.djangoproject.com/documentation/testing/
|
.. _Testing Django applications: ../testing/
|
||||||
|
|
||||||
Running the unit tests
|
Running the unit tests
|
||||||
----------------------
|
----------------------
|
||||||
@ -338,21 +433,63 @@ trunk more than once.
|
|||||||
Using branches
|
Using branches
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
To test a given branch, you can simply check out the entire branch, like so::
|
To use a branch, you'll need to do two things:
|
||||||
|
|
||||||
|
* Get the branch's code through Subversion.
|
||||||
|
|
||||||
|
* Point your Python ``site-packages`` directory at the branch's version of
|
||||||
|
the ``django`` package rather than the version you already have
|
||||||
|
installed.
|
||||||
|
|
||||||
|
Getting the code from Subversion
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To get the latest version of a branch's code, check it out using Subversion::
|
||||||
|
|
||||||
svn co http://code.djangoproject.com/svn/django/branches/<branch>/
|
svn co http://code.djangoproject.com/svn/django/branches/<branch>/
|
||||||
|
|
||||||
Or, if you've got a working directory you'd like to switch to use a branch,
|
...where ``<branch>`` is the branch's name. See the `list of branch names`_.
|
||||||
you can use::
|
|
||||||
|
Alternatively, you can automatically convert an existing directory of the
|
||||||
|
Django source code as long as you've checked it out via Subversion. To do the
|
||||||
|
conversion, execute this command from within your ``django`` directory::
|
||||||
|
|
||||||
svn switch http://code.djangoproject.com/svn/django/branches/<branch>/
|
svn switch http://code.djangoproject.com/svn/django/branches/<branch>/
|
||||||
|
|
||||||
...in the root of your Django sandbox (the directory that contains ``django``,
|
|
||||||
``docs``, and ``tests``).
|
|
||||||
|
|
||||||
The advantage of using ``svn switch`` instead of ``svn co`` is that the
|
The advantage of using ``svn switch`` instead of ``svn co`` is that the
|
||||||
``switch`` command retains any changes you might have made to your local copy
|
``switch`` command retains any changes you might have made to your local copy
|
||||||
of the code. It attempts to merge those changes into the "switched" code.
|
of the code. It attempts to merge those changes into the "switched" code. The
|
||||||
|
disadvantage is that it may cause conflicts with your local changes if the
|
||||||
|
"switched" code has altered the same lines of code.
|
||||||
|
|
||||||
|
(Note that if you use ``svn switch``, you don't need to point Python at the new
|
||||||
|
version, as explained in the next section.)
|
||||||
|
|
||||||
|
.. _list of branch names: http://code.djangoproject.com/browser/django/branches
|
||||||
|
|
||||||
|
Pointing Python at the new Django version
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Once you've retrieved the branch's code, you'll need to change your Python
|
||||||
|
``site-packages`` directory so that it points to the branch version of the
|
||||||
|
``django`` directory. (The ``site-packages`` directory is somewhere such as
|
||||||
|
``/usr/lib/python2.4/site-packages`` or
|
||||||
|
``/usr/local/lib/python2.4/site-packages`` or ``C:\Python\site-packages``.)
|
||||||
|
|
||||||
|
The simplest way to do this is by renaming the old ``django`` directory to
|
||||||
|
``django.OLD`` and moving the trunk version of the code into the directory
|
||||||
|
and calling it ``django``.
|
||||||
|
|
||||||
|
Alternatively, you can use a symlink called ``django`` that points to the
|
||||||
|
location of the branch's ``django`` package. If you want to switch back, just
|
||||||
|
change the symlink to point to the old code.
|
||||||
|
|
||||||
|
If you're using Django 0.95 or earlier and installed it using
|
||||||
|
``python setup.py install``, you'll have a directory called something like
|
||||||
|
``Django-0.95-py2.4.egg`` instead of ``django``. In this case, edit the file
|
||||||
|
``setuptools.pth`` and remove the line that references the Django ``.egg``
|
||||||
|
file. Then copy the branch's version of the ``django`` directory into
|
||||||
|
``site-packages``.
|
||||||
|
|
||||||
Official releases
|
Official releases
|
||||||
=================
|
=================
|
||||||
@ -457,8 +594,7 @@ requests for commit access are potential flame-war starters, and will be ignored
|
|||||||
.. _search the tracker: http://code.djangoproject.com/search
|
.. _search the tracker: http://code.djangoproject.com/search
|
||||||
.. _django-users: http://groups.google.com/group/django-users
|
.. _django-users: http://groups.google.com/group/django-users
|
||||||
.. _`#django`: irc://irc.freenode.net/django
|
.. _`#django`: irc://irc.freenode.net/django
|
||||||
.. _list of tickets with patches: http://code.djangoproject.com/report/12
|
.. _list of tickets with patches: http://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority
|
||||||
.. _PEP 8: http://www.python.org/peps/pep-0008.html
|
.. _PEP 8: http://www.python.org/peps/pep-0008.html
|
||||||
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
|
|
||||||
.. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n
|
.. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n
|
||||||
.. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases
|
.. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
=====================================
|
=====================================
|
||||||
Cross Site Request Forgery Protection
|
Cross Site Request Forgery protection
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
The CsrfMiddleware class provides easy-to-use protection against
|
The CsrfMiddleware class provides easy-to-use protection against
|
||||||
@ -12,11 +12,11 @@ The first defense against CSRF attacks is to ensure that GET requests
|
|||||||
are side-effect free. POST requests can then be protected by adding this
|
are side-effect free. POST requests can then be protected by adding this
|
||||||
middleware into your list of installed middleware.
|
middleware into your list of installed middleware.
|
||||||
|
|
||||||
|
|
||||||
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
|
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
|
||||||
|
|
||||||
How to use it
|
How to use it
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
|
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
|
||||||
your list of middleware classes, ``MIDDLEWARE_CLASSES``. It needs to process
|
your list of middleware classes, ``MIDDLEWARE_CLASSES``. It needs to process
|
||||||
the response after the SessionMiddleware, so must come before it in the
|
the response after the SessionMiddleware, so must come before it in the
|
||||||
@ -25,6 +25,7 @@ happen to the response, so it must come after GZipMiddleware in the list.
|
|||||||
|
|
||||||
How it works
|
How it works
|
||||||
============
|
============
|
||||||
|
|
||||||
CsrfMiddleware does two things:
|
CsrfMiddleware does two things:
|
||||||
|
|
||||||
1. It modifies outgoing requests by adding a hidden form field to all
|
1. It modifies outgoing requests by adding a hidden form field to all
|
||||||
@ -55,6 +56,7 @@ are modified.
|
|||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
===========
|
===========
|
||||||
|
|
||||||
CsrfMiddleware requires Django's session framework to work. If you have
|
CsrfMiddleware requires Django's session framework to work. If you have
|
||||||
a custom authentication system that manually sets cookies and the like,
|
a custom authentication system that manually sets cookies and the like,
|
||||||
it won't help you.
|
it won't help you.
|
||||||
@ -65,4 +67,3 @@ you might bypass the filter that adds the hidden field to the form,
|
|||||||
in which case form submission will always fail. It may still be possible
|
in which case form submission will always fail. It may still be possible
|
||||||
to use the middleware, provided you can find some way to get the
|
to use the middleware, provided you can find some way to get the
|
||||||
CSRF token and ensure that is included when your form is submitted.
|
CSRF token and ensure that is included when your form is submitted.
|
||||||
|
|
||||||
|
@ -143,9 +143,9 @@ or ``UPDATE`` SQL statements. Specifically, when you call ``save()``, Django
|
|||||||
follows this algorithm:
|
follows this algorithm:
|
||||||
|
|
||||||
* If the object's primary key attribute is set to a value that evaluates to
|
* If the object's primary key attribute is set to a value that evaluates to
|
||||||
``False`` (such as ``None`` or the empty string), Django executes a
|
``True`` (i.e., a value other than ``None`` or the empty string), Django
|
||||||
``SELECT`` query to determine whether a record with the given primary key
|
executes a ``SELECT`` query to determine whether a record with the given
|
||||||
already exists.
|
primary key already exists.
|
||||||
* If the record with the given primary key does already exist, Django
|
* If the record with the given primary key does already exist, Django
|
||||||
executes an ``UPDATE`` query.
|
executes an ``UPDATE`` query.
|
||||||
* If the object's primary key attribute is *not* set, or if it's set but a
|
* If the object's primary key attribute is *not* set, or if it's set but a
|
||||||
@ -526,6 +526,21 @@ Examples::
|
|||||||
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
|
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
|
||||||
[datetime.datetime(2005, 3, 20)]
|
[datetime.datetime(2005, 3, 20)]
|
||||||
|
|
||||||
|
``none()``
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Returns an ``EmptyQuerySet`` -- a ``QuerySet`` that always evaluates to
|
||||||
|
an empty list. This can be used in cases where you know that you should
|
||||||
|
return an empty result set and your caller is expecting a ``QuerySet``
|
||||||
|
object (instead of returning an empty list, for example.)
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
>>> Entry.objects.none()
|
||||||
|
[]
|
||||||
|
|
||||||
``select_related()``
|
``select_related()``
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -1704,6 +1719,46 @@ For every ``ImageField``, the object will have ``get_FOO_height()`` and
|
|||||||
``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This
|
``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This
|
||||||
returns the height (or width) of the image, as an integer, in pixels.
|
returns the height (or width) of the image, as an integer, in pixels.
|
||||||
|
|
||||||
|
Shortcuts
|
||||||
|
=========
|
||||||
|
|
||||||
|
As you develop views, you will discover a number of common idioms in the
|
||||||
|
way you use the database API. Django encodes some of these idioms as
|
||||||
|
shortcuts that can be used to simplify the process of writing views.
|
||||||
|
|
||||||
|
get_object_or_404()
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
One common idiom to use ``get()`` and raise ``Http404`` if the
|
||||||
|
object doesn't exist. This idiom is captured by ``get_object_or_404()``.
|
||||||
|
This function takes a Django model as its first argument and an
|
||||||
|
arbitrary number of keyword arguments, which it passes to the manager's
|
||||||
|
``get()`` function. It raises ``Http404`` if the object doesn't
|
||||||
|
exist. For example::
|
||||||
|
|
||||||
|
# Get the Entry with a primary key of 3
|
||||||
|
e = get_object_or_404(Entry, pk=3)
|
||||||
|
|
||||||
|
When you provide a model to this shortcut function, the default manager
|
||||||
|
is used to execute the underlying ``get()`` query. If you don't want to
|
||||||
|
use the default manager, or you want to search a list of related objects,
|
||||||
|
you can provide ``get_object_or_404()`` with a manager object, instead.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
# Get the author of blog instance `e` with a name of 'Fred'
|
||||||
|
a = get_object_or_404(e.authors, name='Fred')
|
||||||
|
|
||||||
|
# Use a custom manager 'recent_entries' in the search for an
|
||||||
|
# entry with a primary key of 3
|
||||||
|
e = get_object_or_404(Entry.recent_entries, pk=3)
|
||||||
|
|
||||||
|
get_list_or_404()
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
``get_list_or_404`` behaves the same was as ``get_object_or_404()``
|
||||||
|
-- except the it uses using ``filter()`` instead of ``get()``. It raises
|
||||||
|
``Http404`` if the list is empty.
|
||||||
|
|
||||||
Falling back to raw SQL
|
Falling back to raw SQL
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ code.
|
|||||||
|
|
||||||
This is the philosophy behind `template inheritance`_.
|
This is the philosophy behind `template inheritance`_.
|
||||||
|
|
||||||
.. _template inheritance: http://www.djangoproject.com/documentation/templates/#template-inheritance
|
.. _template inheritance: ../templates/#template-inheritance
|
||||||
|
|
||||||
Be decoupled from HTML
|
Be decoupled from HTML
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -53,7 +53,7 @@ Prints the admin-index template snippet for the given appnames.
|
|||||||
Use admin-index template snippets if you want to customize the look and feel of
|
Use admin-index template snippets if you want to customize the look and feel of
|
||||||
your admin's index page. See `Tutorial 2`_ for more information.
|
your admin's index page. See `Tutorial 2`_ for more information.
|
||||||
|
|
||||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
.. _Tutorial 2: ../tutorial2/
|
||||||
|
|
||||||
createcachetable [tablename]
|
createcachetable [tablename]
|
||||||
----------------------------
|
----------------------------
|
||||||
@ -61,7 +61,7 @@ createcachetable [tablename]
|
|||||||
Creates a cache table named ``tablename`` for use with the database cache
|
Creates a cache table named ``tablename`` for use with the database cache
|
||||||
backend. See the `cache documentation`_ for more information.
|
backend. See the `cache documentation`_ for more information.
|
||||||
|
|
||||||
.. _cache documentation: http://www.djangoproject.com/documentation/cache/
|
.. _cache documentation: ../cache/
|
||||||
|
|
||||||
dbshell
|
dbshell
|
||||||
-------
|
-------
|
||||||
@ -190,7 +190,7 @@ By default, the development server doesn't serve any static files for your site
|
|||||||
you want to configure Django to serve static media, read the `serving static files`_
|
you want to configure Django to serve static media, read the `serving static files`_
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
.. _serving static files: http://www.djangoproject.com/documentation/static_files/
|
.. _serving static files: ../static_files/
|
||||||
|
|
||||||
Turning off auto-reload
|
Turning off auto-reload
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -253,6 +253,8 @@ files are piped directly into the database after all of the models'
|
|||||||
table-creation statements have been executed. Use this SQL hook to populate
|
table-creation statements have been executed. Use this SQL hook to populate
|
||||||
tables with any necessary initial records, SQL functions or test data.
|
tables with any necessary initial records, SQL functions or test data.
|
||||||
|
|
||||||
|
Note that the order in which the SQL files are processed is undefined.
|
||||||
|
|
||||||
sqlreset [appname appname ...]
|
sqlreset [appname appname ...]
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
|
@ -101,9 +101,9 @@ The "From:" header of the e-mail will be the value of the `SERVER_EMAIL setting`
|
|||||||
|
|
||||||
This method exists for convenience and readability.
|
This method exists for convenience and readability.
|
||||||
|
|
||||||
.. _ADMINS setting: http://www.djangoproject.com/documentation/settings/#admins
|
.. _ADMINS setting: ../settings/#admins
|
||||||
.. _EMAIL_SUBJECT_PREFIX setting: http://www.djangoproject.com/documentation/settings/#email-subject-prefix
|
.. _EMAIL_SUBJECT_PREFIX setting: ../settings/#email-subject-prefix
|
||||||
.. _SERVER_EMAIL setting: http://www.djangoproject.com/documentation/settings/#server-email
|
.. _SERVER_EMAIL setting: ../settings/#server-email
|
||||||
|
|
||||||
mail_managers() function
|
mail_managers() function
|
||||||
========================
|
========================
|
||||||
@ -114,7 +114,7 @@ Here's the definition::
|
|||||||
|
|
||||||
mail_managers(subject, message, fail_silently=False)
|
mail_managers(subject, message, fail_silently=False)
|
||||||
|
|
||||||
.. _MANAGERS setting: http://www.djangoproject.com/documentation/settings/#managers
|
.. _MANAGERS setting: ../settings/#managers
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
========
|
========
|
||||||
|
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
|
The framework cleanly separates components such as its database layer and
|
||||||
application layer. And it ships with a simple-yet-powerful `cache framework`_.
|
application layer. And it ships with a simple-yet-powerful `cache framework`_.
|
||||||
|
|
||||||
.. _`cache framework`: http://www.djangoproject.com/documentation/cache/
|
.. _`cache framework`: ../cache/
|
||||||
|
|
||||||
Who's behind this?
|
Who's behind this?
|
||||||
------------------
|
------------------
|
||||||
@ -191,7 +191,7 @@ Like we said: We're picky.
|
|||||||
|
|
||||||
We've documented our philosophies on the `design philosophies page`_.
|
We've documented our philosophies on the `design philosophies page`_.
|
||||||
|
|
||||||
.. _design philosophies page: http://www.djangoproject.com/documentation/design_philosophies/
|
.. _design philosophies page: ../design_philosophies/
|
||||||
|
|
||||||
Do you have any of those nifty "screencast" things?
|
Do you have any of those nifty "screencast" things?
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
@ -277,9 +277,9 @@ How do I get started?
|
|||||||
run into trouble.
|
run into trouble.
|
||||||
|
|
||||||
.. _`Download the code`: http://www.djangoproject.com/download/
|
.. _`Download the code`: http://www.djangoproject.com/download/
|
||||||
.. _`installation guide`: http://www.djangoproject.com/documentation/install/
|
.. _`installation guide`: ../install/
|
||||||
.. _tutorial: http://www.djangoproject.com/documentation/tutorial1/
|
.. _tutorial: ../tutorial1/
|
||||||
.. _documentation: http://www.djangoproject.com/documentation/
|
.. _documentation: ../
|
||||||
.. _ask questions: http://www.djangoproject.com/community/
|
.. _ask questions: http://www.djangoproject.com/community/
|
||||||
|
|
||||||
How do I fix the "install a later version of setuptools" error?
|
How do I fix the "install a later version of setuptools" error?
|
||||||
@ -337,7 +337,7 @@ If you just want to play around and develop things on your local computer, use
|
|||||||
the development Web server that comes with Django. Things should Just Work.
|
the development Web server that comes with Django. Things should Just Work.
|
||||||
|
|
||||||
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
||||||
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/
|
.. _How to use Django with FastCGI: ../fastcgi/
|
||||||
.. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
.. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
||||||
|
|
||||||
How do I install mod_python on Windows?
|
How do I install mod_python on Windows?
|
||||||
@ -464,7 +464,7 @@ Can I use Django with a pre-existing database?
|
|||||||
|
|
||||||
Yes. See `Integrating with a legacy database`_.
|
Yes. See `Integrating with a legacy database`_.
|
||||||
|
|
||||||
.. _`Integrating with a legacy database`: http://www.djangoproject.com/documentation/legacy_databases/
|
.. _`Integrating with a legacy database`: ../legacy_databases/
|
||||||
|
|
||||||
If I make changes to a model, how do I update the database?
|
If I make changes to a model, how do I update the database?
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
@ -511,7 +511,7 @@ type, create an initial data file and put something like this in it::
|
|||||||
As explained in the `SQL initial data file`_ documentation, this SQL file can
|
As explained in the `SQL initial data file`_ documentation, this SQL file can
|
||||||
contain arbitrary SQL, so you can make any sorts of changes you need to make.
|
contain arbitrary SQL, so you can make any sorts of changes you need to make.
|
||||||
|
|
||||||
.. _SQL initial data file: http://www.djangoproject.com/documentation/model_api/#providing-initial-sql-data
|
.. _SQL initial data file: ../model_api/#providing-initial-sql-data
|
||||||
|
|
||||||
Why is Django leaking memory?
|
Why is Django leaking memory?
|
||||||
-----------------------------
|
-----------------------------
|
||||||
@ -592,7 +592,7 @@ My admin-site CSS and images showed up fine using the development server, but th
|
|||||||
See `serving the admin files`_ in the "How to use Django with mod_python"
|
See `serving the admin files`_ in the "How to use Django with mod_python"
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
.. _serving the admin files: http://www.djangoproject.com/documentation/modpython/#serving-the-admin-files
|
.. _serving the admin files: ../modpython/#serving-the-admin-files
|
||||||
|
|
||||||
My "list_filter" contains a ManyToManyField, but the filter doesn't display.
|
My "list_filter" contains a ManyToManyField, but the filter doesn't display.
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
@ -630,7 +630,7 @@ site is built using semantic HTML and plenty of CSS hooks, so any changes you'd
|
|||||||
like to make should be possible by editing the stylesheet. We've got a
|
like to make should be possible by editing the stylesheet. We've got a
|
||||||
`guide to the CSS used in the admin`_ to get you started.
|
`guide to the CSS used in the admin`_ to get you started.
|
||||||
|
|
||||||
.. _`guide to the CSS used in the admin`: http://www.djangoproject.com/documentation/admin_css/
|
.. _`guide to the CSS used in the admin`: ../admin_css/
|
||||||
|
|
||||||
How do I create users without having to edit password hashes?
|
How do I create users without having to edit password hashes?
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
@ -640,7 +640,7 @@ development version, where this problem was fixed on Aug. 4, 2006.
|
|||||||
|
|
||||||
You can also use the Python API. See `creating users`_ for full info.
|
You can also use the Python API. See `creating users`_ for full info.
|
||||||
|
|
||||||
.. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users
|
.. _creating users: ../authentication/#creating-users
|
||||||
|
|
||||||
Contributing code
|
Contributing code
|
||||||
=================
|
=================
|
||||||
@ -651,7 +651,7 @@ How can I get started contributing code to Django?
|
|||||||
Thanks for asking! We've written an entire document devoted to this question.
|
Thanks for asking! We've written an entire document devoted to this question.
|
||||||
It's titled `Contributing to Django`_.
|
It's titled `Contributing to Django`_.
|
||||||
|
|
||||||
.. _Contributing to Django: http://www.djangoproject.com/documentation/contributing/
|
.. _Contributing to Django: ../contributing/
|
||||||
|
|
||||||
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
|
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
|
||||||
--------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------
|
||||||
|
@ -17,7 +17,7 @@ served with no startup time. Unlike mod_python (or `mod_perl`_), a FastCGI
|
|||||||
process doesn't run inside the Web server process, but in a separate,
|
process doesn't run inside the Web server process, but in a separate,
|
||||||
persistent process.
|
persistent process.
|
||||||
|
|
||||||
.. _current preferred setup: http://www.djangoproject.com/documentation/modpython/
|
.. _current preferred setup: ../modpython/
|
||||||
.. _Apache: http://httpd.apache.org/
|
.. _Apache: http://httpd.apache.org/
|
||||||
.. _mod_python: http://www.modpython.org/
|
.. _mod_python: http://www.modpython.org/
|
||||||
.. _mod_perl: http://perl.apache.org/
|
.. _mod_perl: http://perl.apache.org/
|
||||||
|
@ -29,8 +29,8 @@ To install the flatpages app, follow these steps:
|
|||||||
to your MIDDLEWARE_CLASSES_ setting.
|
to your MIDDLEWARE_CLASSES_ setting.
|
||||||
3. Run the command ``manage.py syncdb``.
|
3. Run the command ``manage.py syncdb``.
|
||||||
|
|
||||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||||
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes
|
.. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
|
||||||
|
|
||||||
How it works
|
How it works
|
||||||
============
|
============
|
||||||
@ -63,9 +63,9 @@ resort.
|
|||||||
|
|
||||||
For more on middleware, read the `middleware docs`_.
|
For more on middleware, read the `middleware docs`_.
|
||||||
|
|
||||||
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id
|
.. _SITE_ID: ../settings/#site-id
|
||||||
.. _RequestContext: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
|
.. _RequestContext: ../templates_python/#subclassing-context-djangocontext
|
||||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware docs: ../middleware/
|
||||||
|
|
||||||
How to add, change and delete flatpages
|
How to add, change and delete flatpages
|
||||||
=======================================
|
=======================================
|
||||||
@ -84,9 +84,9 @@ Flatpages are represented by a standard `Django model`_, which lives in
|
|||||||
`django/contrib/flatpages/models.py`_. You can access flatpage objects via the
|
`django/contrib/flatpages/models.py`_. You can access flatpage objects via the
|
||||||
`Django database API`_.
|
`Django database API`_.
|
||||||
|
|
||||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
.. _Django model: ../model_api/
|
||||||
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
|
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
|
||||||
.. _Django database API: http://www.djangoproject.com/documentation/db_api/
|
.. _Django database API: ../db_api/
|
||||||
|
|
||||||
Flatpage templates
|
Flatpage templates
|
||||||
==================
|
==================
|
||||||
|
@ -2,15 +2,27 @@
|
|||||||
Forms, fields, and manipulators
|
Forms, fields, and manipulators
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
|
Forwards-compatibility note
|
||||||
|
===========================
|
||||||
|
|
||||||
|
The legacy forms/manipulators system described in this document is going to be
|
||||||
|
replaced in the next Django release. If you're starting from scratch, we
|
||||||
|
strongly encourage you not to waste your time learning this. Instead, learn and
|
||||||
|
use the django.newforms system, which we have begun to document in the
|
||||||
|
`newforms documentation`_.
|
||||||
|
|
||||||
|
If you have legacy form/manipulator code, read the "Migration plan" section in
|
||||||
|
that document to understand how we're making the switch.
|
||||||
|
|
||||||
|
.. _newforms documentation: ../newforms/
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
Once you've got a chance to play with Django's admin interface, you'll probably
|
Once you've got a chance to play with Django's admin interface, you'll probably
|
||||||
wonder if the fantastic form validation framework it uses is available to user
|
wonder if the fantastic form validation framework it uses is available to user
|
||||||
code. It is, and this document explains how the framework works.
|
code. It is, and this document explains how the framework works.
|
||||||
|
|
||||||
.. admonition:: A note to the lazy
|
|
||||||
|
|
||||||
If all you want to do is present forms for a user to create and/or
|
|
||||||
update a given object, you may be able to use `generic views`_.
|
|
||||||
|
|
||||||
We'll take a top-down approach to examining Django's form validation framework,
|
We'll take a top-down approach to examining Django's form validation framework,
|
||||||
because much of the time you won't need to use the lower-level APIs. Throughout
|
because much of the time you won't need to use the lower-level APIs. Throughout
|
||||||
this document, we'll be working with the following model, a "place" object::
|
this document, we'll be working with the following model, a "place" object::
|
||||||
@ -41,17 +53,17 @@ this document, we'll be working with the following model, a "place" object::
|
|||||||
Defining the above class is enough to create an admin interface to a ``Place``,
|
Defining the above class is enough to create an admin interface to a ``Place``,
|
||||||
but what if you want to allow public users to submit places?
|
but what if you want to allow public users to submit places?
|
||||||
|
|
||||||
Manipulators
|
Automatic Manipulators
|
||||||
============
|
======================
|
||||||
|
|
||||||
The highest-level interface for object creation and modification is the
|
The highest-level interface for object creation and modification is the
|
||||||
**Manipulator** framework. A manipulator is a utility class tied to a given
|
**automatic Manipulator** framework. An automatic manipulator is a utility
|
||||||
model that "knows" how to create or modify instances of that model and how to
|
class tied to a given model that "knows" how to create or modify instances of
|
||||||
validate data for the object. Manipulators come in two flavors:
|
that model and how to validate data for the object. Automatic Manipulators come
|
||||||
``AddManipulators`` and ``ChangeManipulators``. Functionally they are quite
|
in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally
|
||||||
similar, but the former knows how to create new instances of the model, while
|
they are quite similar, but the former knows how to create new instances of the
|
||||||
the latter modifies existing instances. Both types of classes are automatically
|
model, while the latter modifies existing instances. Both types of classes are
|
||||||
created when you define a new class::
|
automatically created when you define a new class::
|
||||||
|
|
||||||
>>> from mysite.myapp.models import Place
|
>>> from mysite.myapp.models import Place
|
||||||
>>> Place.AddManipulator
|
>>> Place.AddManipulator
|
||||||
@ -653,6 +665,6 @@ fails. If no message is passed in, a default message is used.
|
|||||||
the executable specified in the ``JING_PATH`` setting (see the settings_
|
the executable specified in the ``JING_PATH`` setting (see the settings_
|
||||||
document for more details).
|
document for more details).
|
||||||
|
|
||||||
.. _`generic views`: http://www.djangoproject.com/documentation/generic_views/
|
.. _`generic views`: ../generic_views/
|
||||||
.. _`models API`: http://www.djangoproject.com/documentation/model_api/
|
.. _`models API`: ../model_api/
|
||||||
.. _settings: http://www.djangoproject.com/documentation/settings/
|
.. _settings: ../settings/
|
||||||
|
@ -71,7 +71,7 @@ are first evaluated, so if you want to pass in a QuerySet via
|
|||||||
``extra_context`` that is always fresh you need to wrap it in a function or
|
``extra_context`` that is always fresh you need to wrap it in a function or
|
||||||
lambda that returns the QuerySet.
|
lambda that returns the QuerySet.
|
||||||
|
|
||||||
.. _database API docs: http://www.djangoproject.com/documentation/db_api/
|
.. _database API docs: ../db_api/
|
||||||
|
|
||||||
"Simple" generic views
|
"Simple" generic views
|
||||||
======================
|
======================
|
||||||
@ -223,7 +223,7 @@ In addition to ``extra_context``, the template's context will be:
|
|||||||
by ``date_field``. For example, if ``num_latest`` is ``10``, then
|
by ``date_field``. For example, if ``num_latest`` is ``10``, then
|
||||||
``latest`` will be a list of the latest 10 objects in ``queryset``.
|
``latest`` will be a list of the latest 10 objects in ``queryset``.
|
||||||
|
|
||||||
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
|
.. _RequestContext docs: ../templates_python/#subclassing-context-djangocontext
|
||||||
|
|
||||||
``django.views.generic.date_based.archive_year``
|
``django.views.generic.date_based.archive_year``
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
@ -902,7 +902,7 @@ If ``template_name`` isn't specified, this view will use the template
|
|||||||
|
|
||||||
In addition to ``extra_context``, the template's context will be:
|
In addition to ``extra_context``, the template's context will be:
|
||||||
|
|
||||||
* ``form``: A ``django.forms.FormWrapper`` instance representing the form
|
* ``form``: A ``django.oldforms.FormWrapper`` instance representing the form
|
||||||
for editing the object. This lets you refer to form fields easily in the
|
for editing the object. This lets you refer to form fields easily in the
|
||||||
template system.
|
template system.
|
||||||
|
|
||||||
@ -916,8 +916,8 @@ In addition to ``extra_context``, the template's context will be:
|
|||||||
See the `manipulator and formfield documentation`_ for more information
|
See the `manipulator and formfield documentation`_ for more information
|
||||||
about using ``FormWrapper`` objects in templates.
|
about using ``FormWrapper`` objects in templates.
|
||||||
|
|
||||||
.. _authentication system: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication system: ../authentication/
|
||||||
.. _manipulator and formfield documentation: http://www.djangoproject.com/documentation/forms/
|
.. _manipulator and formfield documentation: ../forms/
|
||||||
|
|
||||||
``django.views.generic.create_update.update_object``
|
``django.views.generic.create_update.update_object``
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
@ -984,7 +984,7 @@ If ``template_name`` isn't specified, this view will use the template
|
|||||||
|
|
||||||
In addition to ``extra_context``, the template's context will be:
|
In addition to ``extra_context``, the template's context will be:
|
||||||
|
|
||||||
* ``form``: A ``django.forms.FormWrapper`` instance representing the form
|
* ``form``: A ``django.oldforms.FormWrapper`` instance representing the form
|
||||||
for editing the object. This lets you refer to form fields easily in the
|
for editing the object. This lets you refer to form fields easily in the
|
||||||
template system.
|
template system.
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ internationalization machinery. See the `documentation for USE_I18N`_.
|
|||||||
You'll probably also want to remove ``'django.core.context_processors.i18n'``
|
You'll probably also want to remove ``'django.core.context_processors.i18n'``
|
||||||
from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
|
from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
|
||||||
|
|
||||||
.. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n
|
.. _documentation for USE_I18N: ../settings/#use-i18n
|
||||||
|
|
||||||
How to specify translation strings
|
How to specify translation strings
|
||||||
==================================
|
==================================
|
||||||
@ -175,7 +175,7 @@ class, though::
|
|||||||
verbose_name = _('my thing')
|
verbose_name = _('my thing')
|
||||||
verbose_name_plural = _('mythings')
|
verbose_name_plural = _('mythings')
|
||||||
|
|
||||||
.. _Django models: http://www.djangoproject.com/documentation/model_api/
|
.. _Django models: ../model_api/
|
||||||
|
|
||||||
Pluralization
|
Pluralization
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@ -274,7 +274,7 @@ translation string. Example::
|
|||||||
In this case, both the tag and the filter will see the already-translated
|
In this case, both the tag and the filter will see the already-translated
|
||||||
string, so they don't need to be aware of translations.
|
string, so they don't need to be aware of translations.
|
||||||
|
|
||||||
.. _Django templates: http://www.djangoproject.com/documentation/templates_python/
|
.. _Django templates: ../templates_python/
|
||||||
|
|
||||||
How to create language files
|
How to create language files
|
||||||
============================
|
============================
|
||||||
@ -394,7 +394,7 @@ That's it. Your translations are ready for use.
|
|||||||
please let us know! See `Submitting and maintaining translations`_ for
|
please let us know! See `Submitting and maintaining translations`_ for
|
||||||
the steps to take.
|
the steps to take.
|
||||||
|
|
||||||
.. _Submitting and maintaining translations: http://www.djangoproject.com/documentation/contributing/
|
.. _Submitting and maintaining translations: ../contributing/
|
||||||
|
|
||||||
How Django discovers language preference
|
How Django discovers language preference
|
||||||
========================================
|
========================================
|
||||||
@ -472,7 +472,7 @@ Notes:
|
|||||||
selection to German and English (and any sublanguage, like de-ch or
|
selection to German and English (and any sublanguage, like de-ch or
|
||||||
en-us).
|
en-us).
|
||||||
|
|
||||||
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
|
.. _LANGUAGES setting: ../settings/#languages
|
||||||
|
|
||||||
* If you define a custom ``LANGUAGES`` setting, as explained in the
|
* If you define a custom ``LANGUAGES`` setting, as explained in the
|
||||||
previous bullet, it's OK to mark the languages as translation strings
|
previous bullet, it's OK to mark the languages as translation strings
|
||||||
@ -530,10 +530,10 @@ Note that, with static (middleware-less) translation, the language is in
|
|||||||
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
|
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
|
||||||
in ``request.LANGUAGE_CODE``.
|
in ``request.LANGUAGE_CODE``.
|
||||||
|
|
||||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
.. _settings file: ../settings/
|
||||||
.. _middleware documentation: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware documentation: ../middleware/
|
||||||
.. _session: http://www.djangoproject.com/documentation/sessions/
|
.. _session: ../sessions/
|
||||||
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
.. _request object: ../request_response/#httprequest-objects
|
||||||
|
|
||||||
The ``set_language`` redirect view
|
The ``set_language`` redirect view
|
||||||
==================================
|
==================================
|
||||||
@ -599,7 +599,7 @@ message file. The choice is yours.
|
|||||||
of the settings file to determine this, and a settings file doesn't exist
|
of the settings file to determine this, and a settings file doesn't exist
|
||||||
if you're manually configuring your settings.)
|
if you're manually configuring your settings.)
|
||||||
|
|
||||||
.. _settings documentation: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
|
.. _settings documentation: ../settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||||
|
|
||||||
All message file repositories are structured the same way. They are:
|
All message file repositories are structured the same way. They are:
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ each platform.
|
|||||||
.. _Apache: http://httpd.apache.org/
|
.. _Apache: http://httpd.apache.org/
|
||||||
.. _mod_python: http://www.modpython.org/
|
.. _mod_python: http://www.modpython.org/
|
||||||
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
||||||
.. _How to use Django with mod_python: http://www.djangoproject.com/documentation/modpython/
|
.. _How to use Django with mod_python: ../modpython/
|
||||||
.. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
.. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
||||||
|
|
||||||
Get your database running
|
Get your database running
|
||||||
@ -113,14 +113,14 @@ latest bug fixes and improvements, follow these instructions:
|
|||||||
|
|
||||||
svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django
|
svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django
|
||||||
|
|
||||||
4. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
|
3. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
|
||||||
system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
|
system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
|
||||||
(Windows). This step simply lets you type ``django-admin.py`` from within
|
(Windows). This step simply lets you type ``django-admin.py`` from within
|
||||||
any directory, rather than having to qualify the command with the full path
|
any directory, rather than having to qualify the command with the full path
|
||||||
to the file.
|
to the file.
|
||||||
|
|
||||||
You *don't* have to run ``python setup.py install``, because that command
|
You *don't* have to run ``python setup.py install``, because that command
|
||||||
takes care of steps 3 and 4 for you.
|
takes care of steps 2 and 3 for you.
|
||||||
|
|
||||||
When you want to update your copy of the Django source code, just run the
|
When you want to update your copy of the Django source code, just run the
|
||||||
command ``svn update`` from within the ``django`` directory. When you do this,
|
command ``svn update`` from within the ``django`` directory. When you do this,
|
||||||
|
@ -9,7 +9,7 @@ utilities to automate as much of this process as possible.
|
|||||||
This document assumes you know the Django basics, as covered in the
|
This document assumes you know the Django basics, as covered in the
|
||||||
`official tutorial`_.
|
`official tutorial`_.
|
||||||
|
|
||||||
.. _official tutorial: http://www.djangoproject.com/documentation/tutorial1/
|
.. _official tutorial: ../tutorial1/
|
||||||
|
|
||||||
Give Django your database parameters
|
Give Django your database parameters
|
||||||
====================================
|
====================================
|
||||||
@ -22,18 +22,16 @@ what the name of the database is. Do that by editing these settings in your
|
|||||||
* `DATABASE_ENGINE`_
|
* `DATABASE_ENGINE`_
|
||||||
* `DATABASE_USER`_
|
* `DATABASE_USER`_
|
||||||
* `DATABASE_PASSWORD`_
|
* `DATABASE_PASSWORD`_
|
||||||
* `DATABASE_NAME`_
|
|
||||||
* `DATABASE_HOST`_
|
* `DATABASE_HOST`_
|
||||||
* `DATABASE_PORT`_
|
* `DATABASE_PORT`_
|
||||||
|
|
||||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
.. _settings file: ../settings/
|
||||||
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name
|
.. _DATABASE_NAME: ../settings/#database-name
|
||||||
.. _DATABASE_ENGINE: http://www.djangoproject.com/documentation/settings/#database-engine
|
.. _DATABASE_ENGINE: ../settings/#database-engine
|
||||||
.. _DATABASE_USER: http://www.djangoproject.com/documentation/settings/#database-user
|
.. _DATABASE_USER: ../settings/#database-user
|
||||||
.. _DATABASE_PASSWORD: http://www.djangoproject.com/documentation/settings/#database-password
|
.. _DATABASE_PASSWORD: ../settings/#database-password
|
||||||
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name
|
.. _DATABASE_HOST: ../settings/#database-host
|
||||||
.. _DATABASE_HOST: http://www.djangoproject.com/documentation/settings/#database-host
|
.. _DATABASE_PORT: ../settings/#database-port
|
||||||
.. _DATABASE_PORT: http://www.djangoproject.com/documentation/settings/#database-port
|
|
||||||
|
|
||||||
Auto-generate the models
|
Auto-generate the models
|
||||||
========================
|
========================
|
||||||
@ -54,7 +52,7 @@ Once you've cleaned up your models, name the file ``models.py`` and put it in
|
|||||||
the Python package that holds your app. Then add the app to your
|
the Python package that holds your app. Then add the app to your
|
||||||
``INSTALLED_APPS`` setting.
|
``INSTALLED_APPS`` setting.
|
||||||
|
|
||||||
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin.py documentation: ../django_admin/
|
||||||
|
|
||||||
Install the core Django tables
|
Install the core Django tables
|
||||||
==============================
|
==============================
|
||||||
|
@ -47,7 +47,7 @@ Enables site-wide cache. If this is enabled, each Django-powered page will be
|
|||||||
cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See
|
cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See
|
||||||
the `cache documentation`_.
|
the `cache documentation`_.
|
||||||
|
|
||||||
.. _`cache documentation`: http://www.djangoproject.com/documentation/cache/#the-per-site-cache
|
.. _`cache documentation`: ../cache/#the-per-site-cache
|
||||||
|
|
||||||
django.middleware.common.CommonMiddleware
|
django.middleware.common.CommonMiddleware
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
@ -106,9 +106,10 @@ django.middleware.http.SetRemoteAddrFromForwardedFor
|
|||||||
|
|
||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
Sets ``request['REMOTE_ADDR']`` based on ``request.['HTTP_X_FORWARDED_FOR']``,
|
Sets ``request.META['REMOTE_ADDR']`` based on
|
||||||
if the latter is set. This is useful if you're sitting behind a reverse proxy
|
``request.META['HTTP_X_FORWARDED_FOR']``, if the latter is set. This is useful
|
||||||
that causes each request's ``REMOTE_ADDR`` to be set to ``127.0.0.1``.
|
if you're sitting behind a reverse proxy that causes each request's
|
||||||
|
``REMOTE_ADDR`` to be set to ``127.0.0.1``.
|
||||||
|
|
||||||
**Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're
|
**Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're
|
||||||
not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do
|
not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do
|
||||||
@ -122,7 +123,7 @@ django.contrib.sessions.middleware.SessionMiddleware
|
|||||||
|
|
||||||
Enables session support. See the `session documentation`_.
|
Enables session support. See the `session documentation`_.
|
||||||
|
|
||||||
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
.. _`session documentation`: ../sessions/
|
||||||
|
|
||||||
django.contrib.auth.middleware.AuthenticationMiddleware
|
django.contrib.auth.middleware.AuthenticationMiddleware
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
@ -130,7 +131,7 @@ django.contrib.auth.middleware.AuthenticationMiddleware
|
|||||||
Adds the ``user`` attribute, representing the currently-logged-in user, to
|
Adds the ``user`` attribute, representing the currently-logged-in user, to
|
||||||
every incoming ``HttpRequest`` object. See `Authentication in Web requests`_.
|
every incoming ``HttpRequest`` object. See `Authentication in Web requests`_.
|
||||||
|
|
||||||
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests
|
.. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
|
||||||
|
|
||||||
django.middleware.transaction.TransactionMiddleware
|
django.middleware.transaction.TransactionMiddleware
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
@ -146,7 +147,7 @@ the same transaction control as the view functions.
|
|||||||
|
|
||||||
See the `transaction management documentation`_.
|
See the `transaction management documentation`_.
|
||||||
|
|
||||||
.. _`transaction management documentation`: http://www.djangoproject.com/documentation/transactions/
|
.. _`transaction management documentation`: ../transactions/
|
||||||
|
|
||||||
Writing your own middleware
|
Writing your own middleware
|
||||||
===========================
|
===========================
|
||||||
|
@ -94,7 +94,7 @@ Django places only two restrictions on model field names:
|
|||||||
the way Django's query lookup syntax works. For example::
|
the way Django's query lookup syntax works. For example::
|
||||||
|
|
||||||
class Example(models.Model):
|
class Example(models.Model):
|
||||||
foo__bar = models.IntegerField() 'foo__bar' has two underscores!
|
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
|
||||||
|
|
||||||
These limitations can be worked around, though, because your field name doesn't
|
These limitations can be worked around, though, because your field name doesn't
|
||||||
necessarily have to match your database column name. See `db_column`_ below.
|
necessarily have to match your database column name. See `db_column`_ below.
|
||||||
@ -875,6 +875,10 @@ the relationship should work. All are optional:
|
|||||||
relationship, allowing ``ManyToMany`` relationships to be
|
relationship, allowing ``ManyToMany`` relationships to be
|
||||||
non-symmetrical.
|
non-symmetrical.
|
||||||
|
|
||||||
|
``db_table`` The name of the table to create for storing the many-to-many
|
||||||
|
data. If this is not provided, Django will assume a default
|
||||||
|
name based upon the names of the two tables being joined.
|
||||||
|
|
||||||
======================= ============================================================
|
======================= ============================================================
|
||||||
|
|
||||||
One-to-one relationships
|
One-to-one relationships
|
||||||
@ -1268,6 +1272,24 @@ A few special cases to note about ``list_display``:
|
|||||||
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
|
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
|
||||||
colored_name.allow_tags = True
|
colored_name.allow_tags = True
|
||||||
|
|
||||||
|
* If the string given is a method of the model that returns True or False
|
||||||
|
Django will display a pretty "on" or "off" icon if you give the method a
|
||||||
|
``boolean`` attribute whose value is ``True``.
|
||||||
|
|
||||||
|
Here's a full example model::
|
||||||
|
|
||||||
|
class Person(models.Model):
|
||||||
|
first_name = models.CharField(maxlength=50)
|
||||||
|
birthday = models.DateField()
|
||||||
|
|
||||||
|
class Admin:
|
||||||
|
list_display = ('name', 'born_in_fifties')
|
||||||
|
|
||||||
|
def born_in_fifties(self):
|
||||||
|
return self.birthday.strftime('%Y')[:3] == 5
|
||||||
|
born_in_fifties.boolean = True
|
||||||
|
|
||||||
|
|
||||||
* The ``__str__()`` method is just as valid in ``list_display`` as any
|
* The ``__str__()`` method is just as valid in ``list_display`` as any
|
||||||
other model method, so it's perfectly OK to do this::
|
other model method, so it's perfectly OK to do this::
|
||||||
|
|
||||||
@ -1390,7 +1412,10 @@ This should be set to a list of field names that will be searched whenever
|
|||||||
somebody submits a search query in that text box.
|
somebody submits a search query in that text box.
|
||||||
|
|
||||||
These fields should be some kind of text field, such as ``CharField`` or
|
These fields should be some kind of text field, such as ``CharField`` or
|
||||||
``TextField``.
|
``TextField``. You can also perform a related lookup on a ``ForeignKey`` with
|
||||||
|
the lookup API "follow" notation::
|
||||||
|
|
||||||
|
search_fields = ['foreign_key__related_fieldname']
|
||||||
|
|
||||||
When somebody does a search in the admin search box, Django splits the search
|
When somebody does a search in the admin search box, Django splits the search
|
||||||
query into words and returns all objects that contain each of the words, case
|
query into words and returns all objects that contain each of the words, case
|
||||||
|
@ -20,7 +20,7 @@ You may also be interested in `How to use Django with FastCGI`_.
|
|||||||
.. _mod_perl: http://perl.apache.org/
|
.. _mod_perl: http://perl.apache.org/
|
||||||
.. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html
|
.. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html
|
||||||
.. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html
|
.. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html
|
||||||
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/
|
.. _How to use Django with FastCGI: ../fastcgi/
|
||||||
|
|
||||||
Basic configuration
|
Basic configuration
|
||||||
===================
|
===================
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
The newforms library
|
The newforms library
|
||||||
====================
|
====================
|
||||||
|
|
||||||
``django.newforms`` is a new replacement for ``django.forms``, the old Django
|
``django.newforms`` is Django's fantastic new form-handling library. It's a
|
||||||
form/manipulator/validation framework. This document explains how to use this
|
replacement for ``django.forms``, the old form/manipulator/validation
|
||||||
new form library.
|
framework. This document explains how to use this new library.
|
||||||
|
|
||||||
Migration plan
|
Migration plan
|
||||||
==============
|
==============
|
||||||
@ -13,18 +13,23 @@ Migration plan
|
|||||||
-- i.e., it's not available in the Django 0.95 release. For the next Django
|
-- i.e., it's not available in the Django 0.95 release. For the next Django
|
||||||
release, our plan is to do the following:
|
release, our plan is to do the following:
|
||||||
|
|
||||||
* Move the current ``django.forms`` to ``django.oldforms``. This will allow
|
* As of revision [4208], we've copied the current ``django.forms`` to
|
||||||
for an eased migration of form code. You'll just have to change your
|
``django.oldforms``. This allows you to upgrade your code *now* rather
|
||||||
import statements::
|
than waiting for the backwards-incompatible change and rushing to fix
|
||||||
|
your code after the fact. Just change your import statements like this::
|
||||||
|
|
||||||
from django import forms # old
|
from django import forms # old
|
||||||
from django import oldforms as forms # new
|
from django import oldforms as forms # new
|
||||||
|
|
||||||
* Move the current ``django.newforms`` to ``django.forms``.
|
* At an undecided future date, we will move the current ``django.newforms``
|
||||||
|
to ``django.forms``. This will be a backwards-incompatible change, and
|
||||||
|
anybody who is still using the old version of ``django.forms`` at that
|
||||||
|
time will need to change their import statements, as described in the
|
||||||
|
previous bullet.
|
||||||
|
|
||||||
* We will remove ``django.oldforms`` in the release *after* the next Django
|
* We will remove ``django.oldforms`` in the release *after* the next Django
|
||||||
release -- the release that comes after the release in which we're
|
release -- the release that comes after the release in which we're
|
||||||
creating ``django.oldforms``.
|
creating the new ``django.forms``.
|
||||||
|
|
||||||
With this in mind, we recommend you use the following import statement when
|
With this in mind, we recommend you use the following import statement when
|
||||||
using ``django.newforms``::
|
using ``django.newforms``::
|
||||||
@ -46,9 +51,14 @@ too messy. The choice is yours.
|
|||||||
Overview
|
Overview
|
||||||
========
|
========
|
||||||
|
|
||||||
As the ``django.forms`` system before it, ``django.newforms`` is intended to
|
As with the ``django.forms`` ("manipulators") system before it, ``django.newforms``
|
||||||
handle HTML form display, validation and redisplay. It's what you use if you
|
is intended to handle HTML form display, validation and redisplay. It's what
|
||||||
want to perform server-side validation for an HTML form.
|
you use if you want to perform server-side validation for an HTML form.
|
||||||
|
|
||||||
|
For example, if your Web site has a contact form that visitors can use to
|
||||||
|
send you e-mail, you'd use this library to implement the display of the HTML
|
||||||
|
form fields, along with the form validation. Any time you need to use an HTML
|
||||||
|
``<form>``, you can use this library.
|
||||||
|
|
||||||
The library deals with these concepts:
|
The library deals with these concepts:
|
||||||
|
|
||||||
@ -62,13 +72,751 @@ The library deals with these concepts:
|
|||||||
* **Form** -- A collection of fields that knows how to validate itself and
|
* **Form** -- A collection of fields that knows how to validate itself and
|
||||||
display itself as HTML.
|
display itself as HTML.
|
||||||
|
|
||||||
|
The library is decoupled from the other Django components, such as the database
|
||||||
|
layer, views and templates. It relies only on Django settings, a couple of
|
||||||
|
``django.utils`` helper functions and Django's internationalization hooks (but
|
||||||
|
you're not required to be using internationalization features to use this
|
||||||
|
library).
|
||||||
|
|
||||||
|
Form objects
|
||||||
|
============
|
||||||
|
|
||||||
Using forms with templates
|
The primary way of using the ``newforms`` library is to create a form object.
|
||||||
==========================
|
Do this by subclassing ``django.newforms.Form`` and specifying the form's
|
||||||
|
fields, in a declarative style that you'll be familiar with if you've used
|
||||||
|
Django database models. In this section, we'll iteratively develop a form
|
||||||
|
object that you might use to implement "contact me" functionality on your
|
||||||
|
personal Web site.
|
||||||
|
|
||||||
Using forms in views
|
Start with this basic ``Form`` subclass, which we'll call ``ContactForm``::
|
||||||
====================
|
|
||||||
|
from django import newforms as forms
|
||||||
|
|
||||||
|
class ContactForm(forms.Form):
|
||||||
|
subject = forms.CharField(max_length=100)
|
||||||
|
message = forms.CharField()
|
||||||
|
sender = forms.EmailField()
|
||||||
|
cc_myself = forms.BooleanField()
|
||||||
|
|
||||||
|
A form is composed of ``Field`` objects. In this case, our form has four
|
||||||
|
fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain
|
||||||
|
the different types of fields -- e.g., ``CharField`` and ``EmailField`` --
|
||||||
|
shortly.
|
||||||
|
|
||||||
|
Creating ``Form`` instances
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
A ``Form`` instance is either **bound** or **unbound** to a set of data.
|
||||||
|
|
||||||
|
* If it's **bound** to a set of data, it's capable of validating that data
|
||||||
|
and rendering the form as HTML with the data displayed in the HTML.
|
||||||
|
|
||||||
|
* If it's **unbound**, it cannot do validation (because there's no data to
|
||||||
|
validate!), but it can still render the blank form as HTML.
|
||||||
|
|
||||||
|
To create an unbound ``Form`` instance, simply instantiate the class::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
|
||||||
|
To bind data to a form, pass the data as a dictionary as the first parameter to
|
||||||
|
your ``Form`` class constructor::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
|
||||||
|
In this dictionary, the keys are the field names, which correspond to the
|
||||||
|
attributes in your ``Form`` class. The values are the data you're trying
|
||||||
|
to validate. These will usually be strings, but there's no requirement that
|
||||||
|
they be strings; the type of data you pass depends on the ``Field``, as we'll
|
||||||
|
see in a moment.
|
||||||
|
|
||||||
|
If you need to distinguish between bound and unbound form instances at runtime,
|
||||||
|
check the value of the form's ``is_bound`` attribute::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.is_bound
|
||||||
|
False
|
||||||
|
>>> f = ContactForm({'subject': 'hello'})
|
||||||
|
>>> f.is_bound
|
||||||
|
True
|
||||||
|
|
||||||
|
Note that passing an empty dictionary creates a *bound* form with empty data::
|
||||||
|
|
||||||
|
>>> f = ContactForm({})
|
||||||
|
>>> f.is_bound
|
||||||
|
True
|
||||||
|
|
||||||
|
If you have a bound ``Form`` instance and want to change the data somehow, or
|
||||||
|
if you want to bind an unbound ``Form`` instance to some data, create another
|
||||||
|
``Form`` instance. There is no way to change data in a ``Form`` instance. Once
|
||||||
|
a ``Form`` instance has been created, you should consider its data immutable,
|
||||||
|
whether it has data or not.
|
||||||
|
|
||||||
|
Using forms to validate data
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The primary task of a ``Form`` object is to validate data. With a bound
|
||||||
|
``Form`` instance, call the ``is_valid()`` method to run validation and return
|
||||||
|
a boolean designating whether the data was valid::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
|
||||||
|
Let's try with some invalid data. In this case, ``subject`` is blank (an error,
|
||||||
|
because all fields are required by default) and ``sender`` is not a valid
|
||||||
|
e-mail address::
|
||||||
|
|
||||||
|
>>> data = {'subject': '',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'invalid e-mail address',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
False
|
||||||
|
|
||||||
|
Access the ``Form`` attribute ``errors`` to get a dictionary of error messages::
|
||||||
|
|
||||||
|
>>> f.errors
|
||||||
|
{'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']}
|
||||||
|
|
||||||
|
In this dictionary, the keys are the field names, and the values are lists of
|
||||||
|
Unicode strings representing the error messages. The error messages are stored
|
||||||
|
in lists because a field can have multiple error messages.
|
||||||
|
|
||||||
|
You can access ``errors`` without having to call ``is_valid()`` first. The
|
||||||
|
form's data will be validated the first time either you call ``is_valid()`` or
|
||||||
|
access ``errors``.
|
||||||
|
|
||||||
|
Behavior of unbound forms
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
It's meaningless to validate a form with no data, but, for the record, here's
|
||||||
|
what happens with unbound forms::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.is_valid()
|
||||||
|
False
|
||||||
|
>>> f.errors
|
||||||
|
{}
|
||||||
|
|
||||||
|
Accessing "clean" data
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Each ``Field`` in a ``Form`` class is responsible not only for validating data,
|
||||||
|
but also for "cleaning" it -- normalizing it to a consistent format. This is a
|
||||||
|
nice feature, because it allows data for a particular field to be input in
|
||||||
|
a variety of ways, always resulting in consistent output.
|
||||||
|
|
||||||
|
For example, ``DateField`` normalizes input into a Python ``datetime.date``
|
||||||
|
object. Regardless of whether you pass it a string in the format
|
||||||
|
``'1994-07-15'``, a ``datetime.date`` object or a number of other formats,
|
||||||
|
``DateField`` will always normalize it to a ``datetime.date`` object as long as
|
||||||
|
it's valid.
|
||||||
|
|
||||||
|
Once you've created a ``Form`` instance with a set of data and validated it,
|
||||||
|
you can access the clean data via the ``clean_data`` attribute of the ``Form``
|
||||||
|
object::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> f.clean_data
|
||||||
|
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||||
|
|
||||||
|
Note that any text-based field -- such as ``CharField`` or ``EmailField`` --
|
||||||
|
always cleans the input into a Unicode string. We'll cover the encoding
|
||||||
|
implications later in this document.
|
||||||
|
|
||||||
|
If your data does *not* validate, your ``Form`` instance will not have a
|
||||||
|
``clean_data`` attribute::
|
||||||
|
|
||||||
|
>>> data = {'subject': '',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'invalid e-mail address',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
False
|
||||||
|
>>> f.clean_data
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
||||||
|
|
||||||
|
``clean_data`` will always *only* contain a key for fields defined in the
|
||||||
|
``Form``, even if you pass extra data when you define the ``Form``. In this
|
||||||
|
example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
|
||||||
|
but ``clean_data`` contains only the form's fields::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True,
|
||||||
|
... 'extra_field_1': 'foo',
|
||||||
|
... 'extra_field_2': 'bar',
|
||||||
|
... 'extra_field_3': 'baz'}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> f.clean_data # Doesn't contain extra_field_1, etc.
|
||||||
|
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||||
|
|
||||||
|
Behavior of unbound forms
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
It's meaningless to request "clean" data in a form with no data, but, for the
|
||||||
|
record, here's what happens with unbound forms::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.clean_data
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
||||||
|
|
||||||
|
Outputting forms as HTML
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
The second task of a ``Form`` object is to render itself as HTML. To do so,
|
||||||
|
simply ``print`` it::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> print f
|
||||||
|
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||||
|
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
|
||||||
|
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
|
||||||
|
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
|
||||||
|
|
||||||
|
If the form is bound to data, the HTML output will include that data
|
||||||
|
appropriately. For example, if a field is represented by an
|
||||||
|
``<input type="text">``, the data will be in the ``value`` attribute. If a
|
||||||
|
field is represented by an ``<input type="checkbox">``, then that HTML will
|
||||||
|
include ``checked="checked"`` if appropriate::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> print f
|
||||||
|
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr>
|
||||||
|
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr>
|
||||||
|
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></td></tr>
|
||||||
|
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
|
||||||
|
|
||||||
|
This default output is a two-column HTML table, with a ``<tr>`` for each field.
|
||||||
|
Notice the following:
|
||||||
|
|
||||||
|
* For flexibility, the output does *not* include the ``<table>`` and
|
||||||
|
``</table>`` tags, nor does it include the ``<form>`` and ``</form>``
|
||||||
|
tags or an ``<input type="submit">`` tag. It's your job to do that.
|
||||||
|
|
||||||
|
* Each field type has a default HTML representation. ``CharField`` and
|
||||||
|
``EmailField`` are represented by an ``<input type="text">``.
|
||||||
|
``BooleanField`` is represented by an ``<input type="checkbox">``. Note
|
||||||
|
these are merely sensible defaults; you can specify which HTML to use for
|
||||||
|
a given field by using widgets, which we'll explain shortly.
|
||||||
|
|
||||||
|
* The HTML ``name`` for each tag is taken directly from its attribute name
|
||||||
|
in the ``ContactForm`` class.
|
||||||
|
|
||||||
|
* The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and
|
||||||
|
``'Cc myself:'`` is generated from the field name by converting all
|
||||||
|
underscores to spaces and upper-casing the first letter. Again, note
|
||||||
|
these are merely sensible defaults; you can also specify labels manually.
|
||||||
|
|
||||||
|
* Each text label is surrounded in an HTML ``<label>`` tag, which points
|
||||||
|
to the appropriate form field via its ``id``. Its ``id``, in turn, is
|
||||||
|
generated by prepending ``'id_'`` to the field name. The ``id``
|
||||||
|
attributes and ``<label>`` tags are included in the output by default, to
|
||||||
|
follow best practices, but you can change that behavior.
|
||||||
|
|
||||||
|
Although ``<table>`` output is the default output style when you ``print`` a
|
||||||
|
form, other output styles are available. Each style is available as a method on
|
||||||
|
a form object, and each rendering method returns a Unicode object.
|
||||||
|
|
||||||
|
``as_p()``
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
``Form.as_p()`` renders the form as a series of ``<p>`` tags, with each ``<p>``
|
||||||
|
containing one field::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.as_p()
|
||||||
|
u'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>'
|
||||||
|
>>> print f.as_p()
|
||||||
|
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>
|
||||||
|
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>
|
||||||
|
<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>
|
||||||
|
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
|
||||||
|
|
||||||
|
``as_ul()``
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
``Form.as_ul()`` renders the form as a series of ``<li>`` tags, with each
|
||||||
|
``<li>`` containing one field. It does *not* include the ``<ul>`` or ``</ul>``,
|
||||||
|
so that you can specify any HTML attributes on the ``<ul>`` for flexibility::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.as_ul()
|
||||||
|
u'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>'
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>
|
||||||
|
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>
|
||||||
|
<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>
|
||||||
|
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>
|
||||||
|
|
||||||
|
``as_table()``
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Finally, ``Form.as_table()`` outputs the form as an HTML ``<table>``. This is
|
||||||
|
exactly the same as ``print``. In fact, when you ``print`` a form object, it
|
||||||
|
calls its ``as_table()`` method behind the scenes::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.as_table()
|
||||||
|
u'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>'
|
||||||
|
>>> print f.as_table()
|
||||||
|
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||||
|
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
|
||||||
|
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
|
||||||
|
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
|
||||||
|
|
||||||
|
Configuring HTML ``<label>`` tags
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
An HTML ``<label>`` tag designates which label text is associated with which
|
||||||
|
form element. This small enhancement makes forms more usable and more accessible
|
||||||
|
to assistive devices. It's always a good idea to use ``<label>`` tags.
|
||||||
|
|
||||||
|
By default, the form rendering methods include HTML ``id`` attributes on the
|
||||||
|
form elements and corresponding ``<label>`` tags around the labels. The ``id``
|
||||||
|
attribute values are generated by prepending ``id_`` to the form field names.
|
||||||
|
This behavior is configurable, though, if you want to change the ``id``
|
||||||
|
convention or remove HTML ``id`` attributes and ``<label>`` tags entirely.
|
||||||
|
|
||||||
|
Use the ``auto_id`` argument to the ``Form`` constructor to control the label
|
||||||
|
and ``id`` behavior. This argument must be ``True``, ``False`` or a string.
|
||||||
|
|
||||||
|
If ``auto_id`` is ``False``, then the form output will not include ``<label>``
|
||||||
|
tags nor ``id`` attributes::
|
||||||
|
|
||||||
|
>>> f = ContactForm(auto_id=False)
|
||||||
|
>>> print f.as_table()
|
||||||
|
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /></td></tr>
|
||||||
|
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
|
||||||
|
<tr><th>Sender:</th><td><input type="text" name="sender" /></td></tr>
|
||||||
|
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li>Subject: <input type="text" name="subject" maxlength="100" /></li>
|
||||||
|
<li>Message: <input type="text" name="message" /></li>
|
||||||
|
<li>Sender: <input type="text" name="sender" /></li>
|
||||||
|
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
|
||||||
|
>>> print f.as_p()
|
||||||
|
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
|
||||||
|
<p>Message: <input type="text" name="message" /></p>
|
||||||
|
<p>Sender: <input type="text" name="sender" /></p>
|
||||||
|
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
|
||||||
|
|
||||||
|
If ``auto_id`` is set to ``True``, then the form output *will* include
|
||||||
|
``<label>`` tags and will simply use the field name as its ``id`` for each form
|
||||||
|
field::
|
||||||
|
|
||||||
|
>>> f = ContactForm(auto_id=True)
|
||||||
|
>>> print f.as_table()
|
||||||
|
<tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||||
|
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr>
|
||||||
|
<tr><th><label for="sender">Sender:</label></th><td><input type="text" name="sender" id="sender" /></td></tr>
|
||||||
|
<tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></li>
|
||||||
|
<li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li>
|
||||||
|
<li><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></li>
|
||||||
|
<li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li>
|
||||||
|
>>> print f.as_p()
|
||||||
|
<p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></p>
|
||||||
|
<p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p>
|
||||||
|
<p><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></p>
|
||||||
|
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p>
|
||||||
|
|
||||||
|
If ``auto_id`` is set to a string containing the format character ``'%s'``,
|
||||||
|
then the form output will include ``<label>`` tags, and will generate ``id``
|
||||||
|
attributes based on the format string. For example, for a format string
|
||||||
|
``'field_%s'``, a field named ``subject`` will get the ``id``
|
||||||
|
``'field_subject'``. Continuing our example::
|
||||||
|
|
||||||
|
>>> f = ContactForm(auto_id='id_for_%s')
|
||||||
|
>>> print f.as_table()
|
||||||
|
<tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||||
|
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr>
|
||||||
|
<tr><th><label for="id_for_sender">Sender:</label></th><td><input type="text" name="sender" id="id_for_sender" /></td></tr>
|
||||||
|
<tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
|
||||||
|
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li>
|
||||||
|
<li><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></li>
|
||||||
|
<li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
|
||||||
|
>>> print f.as_p()
|
||||||
|
<p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></p>
|
||||||
|
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p>
|
||||||
|
<p><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></p>
|
||||||
|
<p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p>
|
||||||
|
|
||||||
|
If ``auto_id`` is set to any other true value -- such as a string that doesn't
|
||||||
|
include ``%s`` -- then the library will act as if ``auto_id`` is ``True``.
|
||||||
|
|
||||||
|
By default, ``auto_id`` is set to the string ``'id_%s'``.
|
||||||
|
|
||||||
|
Notes on field ordering
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
In the ``as_p()``, ``as_ul()`` and ``as_table()`` shortcuts, the fields are
|
||||||
|
displayed in the order in which you define them in your form class. For
|
||||||
|
example, in the ``ContactForm`` example, the fields are defined in the order
|
||||||
|
``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML
|
||||||
|
output, just change the order in which those fields are listed in the class.
|
||||||
|
|
||||||
|
How errors are displayed
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you render a bound ``Form`` object, the act of rendering will automatically
|
||||||
|
run the form's validation if it hasn't already happened, and the HTML output
|
||||||
|
will include the validation errors as a ``<ul>`` near the field. The particular
|
||||||
|
positioning of the error messages depends on the output method you're using::
|
||||||
|
|
||||||
|
>>> data = {'subject': '',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'invalid e-mail address',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data, auto_id=False)
|
||||||
|
>>> print f.as_table()
|
||||||
|
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr>
|
||||||
|
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr>
|
||||||
|
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul><input type="text" name="sender" value="invalid e-mail address" /></td></tr>
|
||||||
|
<tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li>
|
||||||
|
<li>Message: <input type="text" name="message" value="Hi there" /></li>
|
||||||
|
<li><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul>Sender: <input type="text" name="sender" value="invalid e-mail address" /></li>
|
||||||
|
<li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li>
|
||||||
|
>>> print f.as_p()
|
||||||
|
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
|
||||||
|
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
|
||||||
|
<p>Message: <input type="text" name="message" value="Hi there" /></p>
|
||||||
|
<p><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul></p>
|
||||||
|
<p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p>
|
||||||
|
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
|
||||||
|
|
||||||
|
More granular output
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``as_p()``, ``as_ul()`` and ``as_table()`` methods are simply shortcuts for
|
||||||
|
lazy developers -- they're not the only way a form object can be displayed.
|
||||||
|
|
||||||
|
To display the HTML for a single field in your form, use dictionary lookup
|
||||||
|
syntax using the field's name as the key, and print the resulting object::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> print f['subject']
|
||||||
|
<input id="id_subject" type="text" name="subject" maxlength="100" />
|
||||||
|
>>> print f['message']
|
||||||
|
<input type="text" name="message" id="id_message" />
|
||||||
|
>>> print f['sender']
|
||||||
|
<input type="text" name="sender" id="id_sender" />
|
||||||
|
>>> print f['cc_myself']
|
||||||
|
<input type="checkbox" name="cc_myself" id="id_cc_myself" />
|
||||||
|
|
||||||
|
Call ``str()`` or ``unicode()`` on the field to get its rendered HTML as a
|
||||||
|
string or Unicode object, respectively::
|
||||||
|
|
||||||
|
>>> str(f['subject'])
|
||||||
|
'<input id="id_subject" type="text" name="subject" maxlength="100" />'
|
||||||
|
>>> unicode(f['subject'])
|
||||||
|
u'<input id="id_subject" type="text" name="subject" maxlength="100" />'
|
||||||
|
|
||||||
|
The field-specific output honors the form object's ``auto_id`` setting::
|
||||||
|
|
||||||
|
>>> f = ContactForm(auto_id=False)
|
||||||
|
>>> print f['message']
|
||||||
|
<input type="text" name="message" />
|
||||||
|
>>> f = ContactForm(auto_id='id_%s')
|
||||||
|
>>> print f['message']
|
||||||
|
<input type="text" name="message" id="id_message" />
|
||||||
|
|
||||||
|
For a field's list of errors, access the field's ``errors`` attribute. This
|
||||||
|
is a list-like object that is displayed as an HTML ``<ul>`` when printed::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
|
||||||
|
>>> f = ContactForm(data, auto_id=False)
|
||||||
|
>>> print f['message']
|
||||||
|
<input type="text" name="message" />
|
||||||
|
>>> f['message'].errors
|
||||||
|
[u'This field is required.']
|
||||||
|
>>> print f['message'].errors
|
||||||
|
<ul class="errorlist"><li>This field is required.</li></ul>
|
||||||
|
>>> f['subject'].errors
|
||||||
|
[]
|
||||||
|
>>> print f['subject'].errors
|
||||||
|
|
||||||
|
>>> str(f['subject'].errors)
|
||||||
|
''
|
||||||
|
|
||||||
|
Fields
|
||||||
|
======
|
||||||
|
|
||||||
|
When you create a ``Form`` class, the most important part is defining the
|
||||||
|
fields of the form. Each field has custom validation logic, along with a few
|
||||||
|
other hooks.
|
||||||
|
|
||||||
|
Although the primary way you'll use ``Field`` classes is in ``Form`` classes,
|
||||||
|
you can also instantiate them and use them directly to get a better idea of
|
||||||
|
how they work. Each ``Field`` instance has a ``clean()`` method, which takes
|
||||||
|
a single argument and either raises a ``django.newforms.ValidationError``
|
||||||
|
exception or returns the clean value::
|
||||||
|
|
||||||
|
>>> f = forms.EmailField()
|
||||||
|
>>> f.clean('foo@example.com')
|
||||||
|
u'foo@example.com'
|
||||||
|
>>> f.clean(u'foo@example.com')
|
||||||
|
u'foo@example.com'
|
||||||
|
>>> f.clean('invalid e-mail address')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid e-mail address.']
|
||||||
|
|
||||||
|
If you've used Django's old forms/validation framework, take care in noticing
|
||||||
|
this ``ValidationError`` is different than the previous ``ValidationError``.
|
||||||
|
This one lives at ``django.newforms.ValidationError`` rather than
|
||||||
|
``django.core.validators.ValidationError``.
|
||||||
|
|
||||||
|
Core field arguments
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Each ``Field`` class constructor takes at least these arguments. Some
|
||||||
|
``Field`` classes take additional, field-specific arguments, but the following
|
||||||
|
should *always* be available:
|
||||||
|
|
||||||
|
``required``
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
By default, each ``Field`` class assumes the value is required, so if you pass
|
||||||
|
an empty value -- either ``None`` or the empty string (``""``) -- then
|
||||||
|
``clean()`` will raise a ``ValidationError`` exception::
|
||||||
|
|
||||||
|
>>> f = forms.CharField()
|
||||||
|
>>> f.clean('foo')
|
||||||
|
u'foo'
|
||||||
|
>>> f.clean('')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean(' ')
|
||||||
|
u' '
|
||||||
|
>>> f.clean(0)
|
||||||
|
u'0'
|
||||||
|
>>> f.clean(True)
|
||||||
|
u'True'
|
||||||
|
>>> f.clean(False)
|
||||||
|
u'False'
|
||||||
|
|
||||||
|
To specify that a field is *not* required, pass ``required=False`` to the
|
||||||
|
``Field`` constructor::
|
||||||
|
|
||||||
|
>>> f = forms.CharField(required=False)
|
||||||
|
>>> f.clean('foo')
|
||||||
|
u'foo'
|
||||||
|
>>> f.clean('')
|
||||||
|
u''
|
||||||
|
>>> f.clean(None)
|
||||||
|
u''
|
||||||
|
>>> f.clean(0)
|
||||||
|
u'0'
|
||||||
|
>>> f.clean(True)
|
||||||
|
u'True'
|
||||||
|
>>> f.clean(False)
|
||||||
|
u'False'
|
||||||
|
|
||||||
|
If a ``Field`` has ``required=False`` and you pass ``clean()`` an empty value,
|
||||||
|
then ``clean()`` will return a *normalized* empty value rather than raising
|
||||||
|
``ValidationError``. For ``CharField``, this will be a Unicode empty string.
|
||||||
|
For other ``Field`` classes, it might be ``None``. (This varies from field to
|
||||||
|
field.)
|
||||||
|
|
||||||
|
``label``
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
The ``label`` argument lets you specify the "human-friendly" label for this
|
||||||
|
field. This is used when the ``Field`` is displayed in a ``Form``.
|
||||||
|
|
||||||
|
As explained in _`Outputting forms as HTML` above, the default label for a
|
||||||
|
``Field`` is generated from the field name by converting all underscores to
|
||||||
|
spaces and upper-casing the first letter. Specify ``label`` if that default
|
||||||
|
behavior doesn't result in an adequate label.
|
||||||
|
|
||||||
|
Here's a full example ``Form`` that implements ``label`` for two of its fields.
|
||||||
|
We've specified ``auto_id=False`` to simplify the output::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField(label='Your name')
|
||||||
|
... url = forms.URLField(label='Your Web site', required=False)
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> f = CommentForm(auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Your name:</th><td><input type="text" name="name" /></td></tr>
|
||||||
|
<tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
|
``initial``
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``initial`` argument lets you specify the initial value to use when
|
||||||
|
rendering this ``Field`` in an unbound ``Form``.
|
||||||
|
|
||||||
|
The use-case for this is when you want to display an "empty" form in which a
|
||||||
|
field is initialized to a particular value. For example::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField(initial='Your name')
|
||||||
|
... url = forms.URLField(initial='http://')
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> f = CommentForm(auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
|
You may be thinking, why not just pass a dictionary of the initial values as
|
||||||
|
data when displaying the form? Well, if you do that, you'll trigger validation,
|
||||||
|
and the HTML output will include any validation errors::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField()
|
||||||
|
... url = forms.URLField()
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> default_data = {'name': 'Your name', 'url': 'http://'}
|
||||||
|
>>> f = CommentForm(default_data, auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
|
This is why ``initial`` values are only displayed for unbound forms. For bound
|
||||||
|
forms, the HTML output will use the bound data.
|
||||||
|
|
||||||
|
Also note that ``initial`` values are *not* used as "fallback" data in
|
||||||
|
validation if a particular field's value is not given. ``initial`` values are
|
||||||
|
*only* intended for initial form display::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField(initial='Your name')
|
||||||
|
... url = forms.URLField(initial='http://')
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> data = {'name': '', 'url': '', 'comment': 'Foo'}
|
||||||
|
>>> f = CommentForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
False
|
||||||
|
# The form does *not* fall back to using the initial values.
|
||||||
|
>>> f.errors
|
||||||
|
{'url': [u'This field is required.'], 'name': [u'This field is required.']}
|
||||||
|
|
||||||
|
``widget``
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``widget`` argument lets you specify a ``Widget`` class to use when
|
||||||
|
rendering this ``Field``. See _`Widgets` below for more information.
|
||||||
|
|
||||||
|
``help_text``
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``help_text`` argument lets you specify descriptive text for this
|
||||||
|
``Field``. If you provide ``help_text``, it will be displayed next to the
|
||||||
|
``Field`` when the ``Field`` is rendered in a ``Form``.
|
||||||
|
|
||||||
|
Here's a full example ``Form`` that implements ``help_text`` for two of its
|
||||||
|
fields. We've specified ``auto_id=False`` to simplify the output::
|
||||||
|
|
||||||
|
>>> class HelpTextContactForm(forms.Form):
|
||||||
|
... subject = forms.CharField(max_length=100, help_text='100 characters max.')
|
||||||
|
... message = forms.CharField()
|
||||||
|
... sender = forms.EmailField(help_text='A valid e-mail address, please.')
|
||||||
|
... cc_myself = forms.BooleanField()
|
||||||
|
>>> f = HelpTextContactForm(auto_id=False)
|
||||||
|
>>> print f.as_table()
|
||||||
|
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr>
|
||||||
|
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
|
||||||
|
<tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid e-mail address, please.</td></tr>
|
||||||
|
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</li>
|
||||||
|
<li>Message: <input type="text" name="message" /></li>
|
||||||
|
<li>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</li>
|
||||||
|
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
|
||||||
|
>>> print f.as_p()
|
||||||
|
<p>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</p>
|
||||||
|
<p>Message: <input type="text" name="message" /></p>
|
||||||
|
<p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p>
|
||||||
|
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
|
||||||
|
|
||||||
|
Dynamic initial values
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The ``initial`` argument to ``Field`` (explained above) lets you hard-code the
|
||||||
|
initial value for a ``Field`` -- but what if you want to declare the initial
|
||||||
|
value at runtime? For example, you might want to fill in a ``username`` field
|
||||||
|
with the username of the current session.
|
||||||
|
|
||||||
|
To accomplish this, use the ``initial`` argument to a ``Form``. This argument,
|
||||||
|
if given, should be a dictionary mapping field names to initial values. Only
|
||||||
|
include the fields for which you're specifying an initial value; it's not
|
||||||
|
necessary to include every field in your form. For example::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField()
|
||||||
|
... url = forms.URLField()
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> f = CommentForm(initial={'name': 'your username'}, auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="your username" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
>>> f = CommentForm(initial={'name': 'another username'}, auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="another username" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
|
Just like the ``initial`` parameter to ``Field``, these values are only
|
||||||
|
displayed for unbound forms, and they're not used as fallback values if a
|
||||||
|
particular value isn't provided.
|
||||||
|
|
||||||
|
Finally, note that if a ``Field`` defines ``initial`` *and* you include
|
||||||
|
``initial`` when instantiating the ``Form``, then the latter ``initial`` will
|
||||||
|
have precedence. In this example, ``initial`` is provided both at the field
|
||||||
|
level and at the form instance level, and the latter gets precedence::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField(initial='class')
|
||||||
|
... url = forms.URLField()
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="instance" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
More coming soon
|
More coming soon
|
||||||
================
|
================
|
||||||
@ -77,3 +825,9 @@ That's all the documentation for now. For more, see the file
|
|||||||
http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py
|
http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py
|
||||||
-- the unit tests for ``django.newforms``. This can give you a good idea of
|
-- the unit tests for ``django.newforms``. This can give you a good idea of
|
||||||
what's possible.
|
what's possible.
|
||||||
|
|
||||||
|
If you're really itching to learn and use this library, please be patient.
|
||||||
|
We're working hard on finishing both the code and documentation.
|
||||||
|
|
||||||
|
Widgets
|
||||||
|
=======
|
||||||
|
@ -24,7 +24,7 @@ and Django's ``HttpResponse`` objects are file-like objects.
|
|||||||
|
|
||||||
For more information on the CSV library, see the `CSV library docs`_.
|
For more information on the CSV library, see the `CSV library docs`_.
|
||||||
|
|
||||||
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/
|
.. _Request and response objects: ../request_response/
|
||||||
.. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html
|
.. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html
|
||||||
|
|
||||||
Here's an example::
|
Here's an example::
|
||||||
@ -115,5 +115,5 @@ a line of CSV for each row. It uses the `addslashes template filter`_ to ensure
|
|||||||
there aren't any problems with quotes. If you can be certain your data doesn't
|
there aren't any problems with quotes. If you can be certain your data doesn't
|
||||||
have single or double quotes in it, you can remove the ``addslashes`` filters.
|
have single or double quotes in it, you can remove the ``addslashes`` filters.
|
||||||
|
|
||||||
.. _Django template system: http://www.djangoproject.com/documentation/templates/
|
.. _Django template system: ../templates/
|
||||||
.. _addslashes template filter: http://www.djangoproject.com/documentation/templates/#addslashes
|
.. _addslashes template filter: ../templates/#addslashes
|
||||||
|
@ -43,7 +43,7 @@ objects.
|
|||||||
For more information on ``HttpResponse`` objects, see
|
For more information on ``HttpResponse`` objects, see
|
||||||
`Request and response objects`_.
|
`Request and response objects`_.
|
||||||
|
|
||||||
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/
|
.. _Request and response objects: ../request_response/
|
||||||
|
|
||||||
Here's a "Hello World" example::
|
Here's a "Hello World" example::
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ understand how Django works, but this isn't intended to be a tutorial or
|
|||||||
reference. Please see our more-detailed Django documentation_ when you're ready
|
reference. Please see our more-detailed Django documentation_ when you're ready
|
||||||
to start a project.
|
to start a project.
|
||||||
|
|
||||||
.. _documentation: http://www.djangoproject.com/documentation/
|
.. _documentation: ../
|
||||||
|
|
||||||
Design your model
|
Design your model
|
||||||
=================
|
=================
|
||||||
|
@ -15,8 +15,8 @@ To install the redirects app, follow these steps:
|
|||||||
to your MIDDLEWARE_CLASSES_ setting.
|
to your MIDDLEWARE_CLASSES_ setting.
|
||||||
3. Run the command ``manage.py syncdb``.
|
3. Run the command ``manage.py syncdb``.
|
||||||
|
|
||||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||||
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes
|
.. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
|
||||||
|
|
||||||
How it works
|
How it works
|
||||||
============
|
============
|
||||||
@ -46,8 +46,8 @@ resort.
|
|||||||
|
|
||||||
For more on middleware, read the `middleware docs`_.
|
For more on middleware, read the `middleware docs`_.
|
||||||
|
|
||||||
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id
|
.. _SITE_ID: ../settings/#site-id
|
||||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware docs: ../middleware/
|
||||||
|
|
||||||
How to add, change and delete redirects
|
How to add, change and delete redirects
|
||||||
=======================================
|
=======================================
|
||||||
@ -63,9 +63,9 @@ Via the Python API
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
Redirects are represented by a standard `Django model`_, which lives in
|
Redirects are represented by a standard `Django model`_, which lives in
|
||||||
`django/contrib/redirects/models/redirects.py`_. You can access redirect
|
`django/contrib/redirects/models.py`_. You can access redirect
|
||||||
objects via the `Django database API`_.
|
objects via the `Django database API`_.
|
||||||
|
|
||||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
.. _Django model: ../model_api/
|
||||||
.. _django/contrib/redirects/models/redirects.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models/redirects.py
|
.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
|
||||||
.. _Django database API: http://www.djangoproject.com/documentation/db_api/
|
.. _Django database API: ../db_api/
|
||||||
|
@ -117,14 +117,14 @@ All attributes except ``session`` should be considered read-only.
|
|||||||
``AuthenticationMiddleware`` activated. For more, see
|
``AuthenticationMiddleware`` activated. For more, see
|
||||||
`Authentication in Web requests`_.
|
`Authentication in Web requests`_.
|
||||||
|
|
||||||
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests
|
.. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
|
||||||
|
|
||||||
``session``
|
``session``
|
||||||
A readable-and-writable, dictionary-like object that represents the current
|
A readable-and-writable, dictionary-like object that represents the current
|
||||||
session. This is only available if your Django installation has session
|
session. This is only available if your Django installation has session
|
||||||
support activated. See the `session documentation`_ for full details.
|
support activated. See the `session documentation`_ for full details.
|
||||||
|
|
||||||
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
.. _`session documentation`: ../sessions/
|
||||||
|
|
||||||
``raw_post_data``
|
``raw_post_data``
|
||||||
The raw HTTP POST data. This is only useful for advanced processing. Use
|
The raw HTTP POST data. This is only useful for advanced processing. Use
|
||||||
|
@ -27,7 +27,7 @@ If you don't want to use sessions, you might as well remove the
|
|||||||
``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
|
``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
|
||||||
from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
|
from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
|
||||||
|
|
||||||
.. _middleware: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware: ../middleware/
|
||||||
|
|
||||||
Using sessions in views
|
Using sessions in views
|
||||||
=======================
|
=======================
|
||||||
@ -217,6 +217,23 @@ browser-length cookies -- cookies that expire as soon as the user closes his or
|
|||||||
her browser. Use this if you want people to have to log in every time they open
|
her browser. Use this if you want people to have to log in every time they open
|
||||||
a browser.
|
a browser.
|
||||||
|
|
||||||
|
Clearing the session table
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Note that session data can accumulate in the ``django_session`` database table
|
||||||
|
and Django does *not* provide automatic purging. Therefore, it's your job to
|
||||||
|
purge expired sessions on a regular basis.
|
||||||
|
|
||||||
|
To understand this problem, consider what happens when a user uses a session.
|
||||||
|
When a user logs in, Django adds a row to the ``django_session`` database
|
||||||
|
table. Django updates this row each time the session data changes. If the user
|
||||||
|
logs out manually, Django deletes the row. But if the user does *not* log out,
|
||||||
|
the row never gets deleted.
|
||||||
|
|
||||||
|
Django provides a sample clean-up script in ``django/bin/daily_cleanup.py``.
|
||||||
|
That script deletes any session in the session table whose ``expire_date`` is
|
||||||
|
in the past -- but your application may have different requirements.
|
||||||
|
|
||||||
Settings
|
Settings
|
||||||
========
|
========
|
||||||
|
|
||||||
@ -273,7 +290,7 @@ Whether to save the session data on every request. If this is ``False``
|
|||||||
(default), then the session data will only be saved if it has been modified --
|
(default), then the session data will only be saved if it has been modified --
|
||||||
that is, if any of its dictionary values have been assigned or deleted.
|
that is, if any of its dictionary values have been assigned or deleted.
|
||||||
|
|
||||||
.. _Django settings: http://www.djangoproject.com/documentation/settings/
|
.. _Django settings: ../settings/
|
||||||
|
|
||||||
Technical details
|
Technical details
|
||||||
=================
|
=================
|
||||||
|
@ -59,7 +59,7 @@ Use the ``--settings`` command-line argument to specify the settings manually::
|
|||||||
|
|
||||||
django-admin.py runserver --settings=mysite.settings
|
django-admin.py runserver --settings=mysite.settings
|
||||||
|
|
||||||
.. _django-admin.py: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin.py: ../django_admin/
|
||||||
|
|
||||||
On the server (mod_python)
|
On the server (mod_python)
|
||||||
--------------------------
|
--------------------------
|
||||||
@ -75,7 +75,7 @@ settings file to use. Do that with ``SetEnv``::
|
|||||||
|
|
||||||
Read the `Django mod_python documentation`_ for more information.
|
Read the `Django mod_python documentation`_ for more information.
|
||||||
|
|
||||||
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/
|
.. _Django mod_python documentation: ../modpython/
|
||||||
|
|
||||||
Default settings
|
Default settings
|
||||||
================
|
================
|
||||||
@ -102,7 +102,7 @@ between the current settings file and Django's default settings.
|
|||||||
|
|
||||||
For more, see the `diffsettings documentation`_.
|
For more, see the `diffsettings documentation`_.
|
||||||
|
|
||||||
.. _diffsettings documentation: http://www.djangoproject.com/documentation/django_admin/#diffsettings
|
.. _diffsettings documentation: ../django_admin/#diffsettings
|
||||||
|
|
||||||
Using settings in Python code
|
Using settings in Python code
|
||||||
=============================
|
=============================
|
||||||
@ -157,13 +157,13 @@ ABSOLUTE_URL_OVERRIDES
|
|||||||
|
|
||||||
Default: ``{}`` (Empty dictionary)
|
Default: ``{}`` (Empty dictionary)
|
||||||
|
|
||||||
A dictionary mapping ``"app_label.module_name"`` strings to functions that take
|
A dictionary mapping ``"app_label.model_name"`` strings to functions that take
|
||||||
a model object and return its URL. This is a way of overriding
|
a model object and return its URL. This is a way of overriding
|
||||||
``get_absolute_url()`` methods on a per-installation basis. Example::
|
``get_absolute_url()`` methods on a per-installation basis. Example::
|
||||||
|
|
||||||
ABSOLUTE_URL_OVERRIDES = {
|
ABSOLUTE_URL_OVERRIDES = {
|
||||||
'blogs.blogs': lambda o: "/blogs/%s/" % o.slug,
|
'blogs.Weblog': lambda o: "/blogs/%s/" % o.slug,
|
||||||
'news.stories': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
|
'news.Story': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
|
||||||
}
|
}
|
||||||
|
|
||||||
ADMIN_FOR
|
ADMIN_FOR
|
||||||
@ -306,7 +306,7 @@ pages -- and, possibly, by other parts of the system. See
|
|||||||
|
|
||||||
See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
||||||
|
|
||||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
.. _allowed date format strings: ../templates/#now
|
||||||
|
|
||||||
DATETIME_FORMAT
|
DATETIME_FORMAT
|
||||||
---------------
|
---------------
|
||||||
@ -319,7 +319,7 @@ pages -- and, possibly, by other parts of the system. See
|
|||||||
|
|
||||||
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
||||||
|
|
||||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
.. _allowed date format strings: ../templates/#now
|
||||||
|
|
||||||
DEBUG
|
DEBUG
|
||||||
-----
|
-----
|
||||||
@ -433,7 +433,7 @@ A tuple of strings designating all applications that are enabled in this Django
|
|||||||
installation. Each string should be a full Python path to a Python package that
|
installation. Each string should be a full Python path to a Python package that
|
||||||
contains a Django application, as created by `django-admin.py startapp`_.
|
contains a Django application, as created by `django-admin.py startapp`_.
|
||||||
|
|
||||||
.. _django-admin.py startapp: http://www.djangoproject.com/documentation/django_admin/#startapp-appname
|
.. _django-admin.py startapp: ../django_admin/#startapp-appname
|
||||||
|
|
||||||
INTERNAL_IPS
|
INTERNAL_IPS
|
||||||
------------
|
------------
|
||||||
@ -464,7 +464,7 @@ A string representing the language code for this installation. This should be
|
|||||||
in standard language format. For example, U.S. English is ``"en-us"``. See the
|
in standard language format. For example, U.S. English is ``"en-us"``. See the
|
||||||
`internationalization docs`_.
|
`internationalization docs`_.
|
||||||
|
|
||||||
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/
|
.. _internationalization docs: ../i18n/
|
||||||
|
|
||||||
LANGUAGES
|
LANGUAGES
|
||||||
---------
|
---------
|
||||||
@ -557,6 +557,11 @@ Default: ``''`` (Empty string)
|
|||||||
URL that handles the media served from ``MEDIA_ROOT``.
|
URL that handles the media served from ``MEDIA_ROOT``.
|
||||||
Example: ``"http://media.lawrence.com"``
|
Example: ``"http://media.lawrence.com"``
|
||||||
|
|
||||||
|
Note that this should have a trailing slash if it has a path component.
|
||||||
|
|
||||||
|
Good: ``"http://www.example.com/static/"``
|
||||||
|
Bad: ``"http://www.example.com/static"``
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES
|
MIDDLEWARE_CLASSES
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@ -612,7 +617,7 @@ Default: Not defined
|
|||||||
A string representing the full Python import path to your root URLconf. For example:
|
A string representing the full Python import path to your root URLconf. For example:
|
||||||
``"mydjangoapps.urls"``. See `How Django processes a request`_.
|
``"mydjangoapps.urls"``. See `How Django processes a request`_.
|
||||||
|
|
||||||
.. _How Django processes a request: http://www.djangoproject.com/documentation/url_dispatch/#how-django-processes-a-request
|
.. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request
|
||||||
|
|
||||||
SECRET_KEY
|
SECRET_KEY
|
||||||
----------
|
----------
|
||||||
@ -704,7 +709,7 @@ and a single database can manage content for multiple sites.
|
|||||||
|
|
||||||
See the `site framework docs`_.
|
See the `site framework docs`_.
|
||||||
|
|
||||||
.. _site framework docs: http://www.djangoproject.com/documentation/sites/
|
.. _site framework docs: ../sites/
|
||||||
|
|
||||||
TEMPLATE_CONTEXT_PROCESSORS
|
TEMPLATE_CONTEXT_PROCESSORS
|
||||||
---------------------------
|
---------------------------
|
||||||
@ -760,7 +765,7 @@ Default: ``''`` (Empty string)
|
|||||||
Output, as a string, that the template system should use for invalid (e.g.
|
Output, as a string, that the template system should use for invalid (e.g.
|
||||||
misspelled) variables. See `How invalid variables are handled`_.
|
misspelled) variables. See `How invalid variables are handled`_.
|
||||||
|
|
||||||
.. _How invalid variables are handled: http://www.djangoproject.com/documentation/templates_python/#how-invalid-variables-are-handled
|
.. _How invalid variables are handled: ../templates_python/#how-invalid-variables-are-handled
|
||||||
|
|
||||||
TEST_RUNNER
|
TEST_RUNNER
|
||||||
-----------
|
-----------
|
||||||
@ -798,7 +803,7 @@ pages -- and, possibly, by other parts of the system. See
|
|||||||
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and
|
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and
|
||||||
MONTH_DAY_FORMAT.
|
MONTH_DAY_FORMAT.
|
||||||
|
|
||||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
.. _allowed date format strings: ../templates/#now
|
||||||
|
|
||||||
TIME_ZONE
|
TIME_ZONE
|
||||||
---------
|
---------
|
||||||
@ -868,11 +873,11 @@ Different locales have different formats. For example, U.S. English would say
|
|||||||
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
|
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
|
||||||
TIME_FORMAT and MONTH_DAY_FORMAT.
|
TIME_FORMAT and MONTH_DAY_FORMAT.
|
||||||
|
|
||||||
.. _cache docs: http://www.djangoproject.com/documentation/cache/
|
.. _cache docs: ../cache/
|
||||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware docs: ../middleware/
|
||||||
.. _session docs: http://www.djangoproject.com/documentation/sessions/
|
.. _session docs: ../sessions/
|
||||||
.. _See available choices: http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
.. _See available choices: http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||||
.. _template documentation: http://www.djangoproject.com/documentation/templates_python/
|
.. _template documentation: ../templates_python/
|
||||||
|
|
||||||
Creating your own settings
|
Creating your own settings
|
||||||
==========================
|
==========================
|
||||||
|
@ -23,8 +23,8 @@ you express this information in Python code.
|
|||||||
It works much like Django's `syndication framework`_. To create a sitemap, just
|
It works much like Django's `syndication framework`_. To create a sitemap, just
|
||||||
write a ``Sitemap`` class and point to it in your URLconf_.
|
write a ``Sitemap`` class and point to it in your URLconf_.
|
||||||
|
|
||||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
.. _syndication framework: ../syndication/
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
@ -41,9 +41,9 @@ To install the sitemap app, follow these steps:
|
|||||||
reason it needs to go into ``INSTALLED_APPS`` is so that the
|
reason it needs to go into ``INSTALLED_APPS`` is so that the
|
||||||
``load_template_source`` template loader can find the default templates.)
|
``load_template_source`` template loader can find the default templates.)
|
||||||
|
|
||||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||||
.. _TEMPLATE_LOADERS: http://www.djangoproject.com/documentation/settings/#template-loaders
|
.. _TEMPLATE_LOADERS: ../settings/#template-loaders
|
||||||
.. _sites framework: http://www.djangoproject.com/documentation/sites/
|
.. _sites framework: ../sites/
|
||||||
|
|
||||||
Initialization
|
Initialization
|
||||||
==============
|
==============
|
||||||
@ -68,7 +68,7 @@ The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
|
|||||||
``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
|
``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
|
||||||
(e.g., ``BlogSitemap(some_var)``).
|
(e.g., ``BlogSitemap(some_var)``).
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Sitemap classes
|
Sitemap classes
|
||||||
===============
|
===============
|
||||||
@ -217,8 +217,8 @@ defined for the current ``SITE_ID`` (see the `sites documentation`_) and
|
|||||||
creates an entry in the sitemap. These entries include only the ``location``
|
creates an entry in the sitemap. These entries include only the ``location``
|
||||||
attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
|
attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
|
||||||
|
|
||||||
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
|
.. _flatpages: ../flatpages/
|
||||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
.. _sites documentation: ../sites/
|
||||||
|
|
||||||
``GenericSitemap``
|
``GenericSitemap``
|
||||||
------------------
|
------------------
|
||||||
@ -232,7 +232,7 @@ the ``lastmod`` attribute in the generated sitemap. You may also pass
|
|||||||
``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
|
``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
|
||||||
constructor to specify these attributes for all URLs.
|
constructor to specify these attributes for all URLs.
|
||||||
|
|
||||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
.. _generic views: ../generic_views/
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
@ -261,7 +261,7 @@ Here's an example of a URLconf_ using both::
|
|||||||
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
||||||
)
|
)
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Creating a sitemap index
|
Creating a sitemap index
|
||||||
========================
|
========================
|
||||||
|
@ -276,8 +276,8 @@ you want your admin site to have access to all objects (not just site-specific
|
|||||||
ones), put ``objects = models.Manager()`` in your model, before you define
|
ones), put ``objects = models.Manager()`` in your model, before you define
|
||||||
``CurrentSiteManager``.
|
``CurrentSiteManager``.
|
||||||
|
|
||||||
.. _manager: http://www.djangoproject.com/documentation/model_api/#managers
|
.. _manager: ../model_api/#managers
|
||||||
.. _manager documentation: http://www.djangoproject.com/documentation/model_api/#managers
|
.. _manager documentation: ../model_api/#managers
|
||||||
|
|
||||||
How Django uses the sites framework
|
How Django uses the sites framework
|
||||||
===================================
|
===================================
|
||||||
@ -316,7 +316,7 @@ Here's how Django uses the sites framework:
|
|||||||
* The shortcut view (``django.views.defaults.shortcut``) uses the domain of
|
* The shortcut view (``django.views.defaults.shortcut``) uses the domain of
|
||||||
the current ``Site`` object when calculating an object's URL.
|
the current ``Site`` object when calculating an object's URL.
|
||||||
|
|
||||||
.. _redirects framework: http://www.djangoproject.com/documentation/redirects/
|
.. _redirects framework: ../redirects/
|
||||||
.. _flatpages framework: http://www.djangoproject.com/documentation/flatpages/
|
.. _flatpages framework: ../flatpages/
|
||||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
.. _syndication framework: ../syndication/
|
||||||
.. _authentication framework: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication framework: ../authentication/
|
||||||
|
@ -24,7 +24,7 @@ production setting. Use this only for development.
|
|||||||
For information on serving static files in an Apache production environment,
|
For information on serving static files in an Apache production environment,
|
||||||
see the `Django mod_python documentation`_.
|
see the `Django mod_python documentation`_.
|
||||||
|
|
||||||
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/#serving-media-files
|
.. _Django mod_python documentation: ../modpython/#serving-media-files
|
||||||
|
|
||||||
How to do it
|
How to do it
|
||||||
============
|
============
|
||||||
@ -49,7 +49,7 @@ Examples:
|
|||||||
* The file ``/path/bar.jpg`` will not be accessible, because it doesn't
|
* The file ``/path/bar.jpg`` will not be accessible, because it doesn't
|
||||||
fall under the document root.
|
fall under the document root.
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Directory listings
|
Directory listings
|
||||||
==================
|
==================
|
||||||
@ -122,4 +122,4 @@ associated with the ``django.views.static.serve`` view. If not
|
|||||||
Of course, the catch here is that you'll have to remember to set ``DEBUG=False``
|
Of course, the catch here is that you'll have to remember to set ``DEBUG=False``
|
||||||
in your production settings file. But you should be doing that anyway.
|
in your production settings file. But you should be doing that anyway.
|
||||||
|
|
||||||
.. _DEBUG setting: http://www.djangoproject.com/documentation/settings/#debug
|
.. _DEBUG setting: ../settings/#debug
|
||||||
|
@ -26,7 +26,7 @@ to determine which feed to output.
|
|||||||
|
|
||||||
To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
|
To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Initialization
|
Initialization
|
||||||
--------------
|
--------------
|
||||||
@ -72,8 +72,8 @@ The above example registers two feeds:
|
|||||||
|
|
||||||
Once that's set up, you just need to define the ``Feed`` classes themselves.
|
Once that's set up, you just need to define the ``Feed`` classes themselves.
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
.. _settings file: ../settings/
|
||||||
|
|
||||||
Feed classes
|
Feed classes
|
||||||
------------
|
------------
|
||||||
@ -156,8 +156,8 @@ put into those elements.
|
|||||||
{{ obj.description }}
|
{{ obj.description }}
|
||||||
|
|
||||||
.. _chicagocrime.org: http://www.chicagocrime.org/
|
.. _chicagocrime.org: http://www.chicagocrime.org/
|
||||||
.. _object-relational mapper: http://www.djangoproject.com/documentation/db_api/
|
.. _object-relational mapper: ../db_api/
|
||||||
.. _Django templates: http://www.djangoproject.com/documentation/templates/
|
.. _Django templates: ../templates/
|
||||||
|
|
||||||
A complex example
|
A complex example
|
||||||
-----------------
|
-----------------
|
||||||
@ -277,7 +277,7 @@ Feeds created by the syndication framework automatically include the
|
|||||||
appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
|
appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
|
||||||
comes directly from your `LANGUAGE_CODE setting`_.
|
comes directly from your `LANGUAGE_CODE setting`_.
|
||||||
|
|
||||||
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code
|
.. _LANGUAGE_CODE setting: ../settings/#language-code
|
||||||
|
|
||||||
URLs
|
URLs
|
||||||
----
|
----
|
||||||
@ -292,7 +292,7 @@ Atom feeds require a ``<link rel="self">`` that defines the feed's current
|
|||||||
location. The syndication framework populates this automatically, using the
|
location. The syndication framework populates this automatically, using the
|
||||||
domain of the current site according to the SITE_ID setting.
|
domain of the current site according to the SITE_ID setting.
|
||||||
|
|
||||||
.. _SITE_ID setting: http://www.djangoproject.com/documentation/settings/#site-id
|
.. _SITE_ID setting: ../settings/#site-id
|
||||||
|
|
||||||
Publishing Atom and RSS feeds in tandem
|
Publishing Atom and RSS feeds in tandem
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
@ -792,7 +792,7 @@ Note that if you use ``{% ssi %}``, you'll need to define
|
|||||||
|
|
||||||
See also: ``{% include %}``.
|
See also: ``{% include %}``.
|
||||||
|
|
||||||
.. _ALLOWED_INCLUDE_ROOTS: http://www.djangoproject.com/documentation/settings/#allowed-include-roots
|
.. _ALLOWED_INCLUDE_ROOTS: ../settings/#allowed-include-roots
|
||||||
|
|
||||||
templatetag
|
templatetag
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
@ -924,13 +924,31 @@ Replaces ampersands with ``&`` entities.
|
|||||||
floatformat
|
floatformat
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
Rounds a floating-point number to one decimal place -- but only if there's a
|
When used without an argument, rounds a floating-point number to one decimal
|
||||||
decimal part to be displayed. For example:
|
place -- but only if there's a decimal part to be displayed. For example:
|
||||||
|
|
||||||
* ``36.123`` gets converted to ``36.1``
|
* ``36.123`` gets converted to ``36.1``
|
||||||
* ``36.15`` gets converted to ``36.2``
|
* ``36.15`` gets converted to ``36.2``
|
||||||
* ``36`` gets converted to ``36``
|
* ``36`` gets converted to ``36``
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
If used with a numeric integer argument, ``floatformat`` rounds a number to that
|
||||||
|
many decimal places. For example:
|
||||||
|
|
||||||
|
* ``36.1234`` with floatformat:3 gets converted to ``36.123``
|
||||||
|
* ``36`` with floatformat:4 gets converted to ``36.0000``
|
||||||
|
|
||||||
|
If the argument passed to ``floatformat`` is negative, it will round a number to
|
||||||
|
that many decimal places -- but only if there's a decimal part to be displayed.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
* ``36.1234`` with floatformat:-3 gets converted to ``36.123``
|
||||||
|
* ``36`` with floatformat:-4 gets converted to ``36``
|
||||||
|
|
||||||
|
Using ``floatformat`` with no argument is equivalent to using ``floatformat`` with
|
||||||
|
an argument of ``-1``.
|
||||||
|
|
||||||
get_digit
|
get_digit
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
||||||
@ -1200,7 +1218,7 @@ django.contrib.humanize
|
|||||||
A set of Django template filters useful for adding a "human touch" to data. See
|
A set of Django template filters useful for adding a "human touch" to data. See
|
||||||
the `humanize documentation`_.
|
the `humanize documentation`_.
|
||||||
|
|
||||||
.. _humanize documentation: http://www.djangoproject.com/documentation/add_ons/#humanize
|
.. _humanize documentation: ../add_ons/#humanize
|
||||||
|
|
||||||
django.contrib.markup
|
django.contrib.markup
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -11,7 +11,7 @@ If you're looking to use the Django template system as part of another
|
|||||||
application -- i.e., without the rest of the framework -- make sure to read
|
application -- i.e., without the rest of the framework -- make sure to read
|
||||||
the `configuration`_ section later in this document.
|
the `configuration`_ section later in this document.
|
||||||
|
|
||||||
.. _`The Django template language: For template authors`: http://www.djangoproject.com/documentation/templates/
|
.. _`The Django template language: For template authors`: ../templates/
|
||||||
|
|
||||||
Basics
|
Basics
|
||||||
======
|
======
|
||||||
@ -327,8 +327,8 @@ Note::
|
|||||||
|
|
||||||
Here's what each of the default processors does:
|
Here's what each of the default processors does:
|
||||||
|
|
||||||
.. _HttpRequest object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
.. _HttpRequest object: ../request_response/#httprequest-objects
|
||||||
.. _TEMPLATE_CONTEXT_PROCESSORS setting: http://www.djangoproject.com/documentation/settings/#template-context-processors
|
.. _TEMPLATE_CONTEXT_PROCESSORS setting: ../settings/#template-context-processors
|
||||||
|
|
||||||
django.core.context_processors.auth
|
django.core.context_processors.auth
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -353,9 +353,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
|||||||
permissions that the currently logged-in user has. See the `permissions
|
permissions that the currently logged-in user has. See the `permissions
|
||||||
docs`_.
|
docs`_.
|
||||||
|
|
||||||
.. _user authentication docs: http://www.djangoproject.com/documentation/authentication/#users
|
.. _user authentication docs: ../authentication/#users
|
||||||
.. _message docs: http://www.djangoproject.com/documentation/authentication/#messages
|
.. _message docs: ../authentication/#messages
|
||||||
.. _permissions docs: http://www.djangoproject.com/documentation/authentication/#permissions
|
.. _permissions docs: ../authentication/#permissions
|
||||||
|
|
||||||
django.core.context_processors.debug
|
django.core.context_processors.debug
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -383,9 +383,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
|||||||
|
|
||||||
See the `internationalization docs`_ for more.
|
See the `internationalization docs`_ for more.
|
||||||
|
|
||||||
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
|
.. _LANGUAGES setting: ../settings/#languages
|
||||||
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code
|
.. _LANGUAGE_CODE setting: ../settings/#language-code
|
||||||
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/
|
.. _internationalization docs: ../i18n/
|
||||||
|
|
||||||
django.core.context_processors.request
|
django.core.context_processors.request
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -944,7 +944,7 @@ The ``takes_context`` parameter defaults to ``False``. When it's set to *True*,
|
|||||||
the tag is passed the context object, as in this example. That's the only
|
the tag is passed the context object, as in this example. That's the only
|
||||||
difference between this case and the previous ``inclusion_tag`` example.
|
difference between this case and the previous ``inclusion_tag`` example.
|
||||||
|
|
||||||
.. _tutorials: http://www.djangoproject.com/documentation/tutorial1/#creating-models
|
.. _tutorials: ../tutorial1/#creating-models
|
||||||
|
|
||||||
Setting a variable in the context
|
Setting a variable in the context
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -1115,5 +1115,5 @@ settings you wish to specify. You might want to consider setting at least
|
|||||||
`settings documentation`_, and any setting starting with *TEMPLATE_*
|
`settings documentation`_, and any setting starting with *TEMPLATE_*
|
||||||
is of obvious interest.
|
is of obvious interest.
|
||||||
|
|
||||||
.. _settings file: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
|
.. _settings file: ../settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||||
.. _settings documentation: http://www.djangoproject.com/documentation/settings/
|
.. _settings documentation: ../settings/
|
||||||
|
@ -13,7 +13,7 @@ changed unexpectedly as a result of the refactor.
|
|||||||
Testing a web application is a complex task, as there are many
|
Testing a web application is a complex task, as there are many
|
||||||
components of a web application that must be validated and tested. To
|
components of a web application that must be validated and tested. To
|
||||||
help you test your application, Django provides a test execution
|
help you test your application, Django provides a test execution
|
||||||
framework, and range of utilities that can be used to stimulate and
|
framework, and range of utilities that can be used to simulate and
|
||||||
inspect various facets of a web application.
|
inspect various facets of a web application.
|
||||||
|
|
||||||
This testing framework is currently under development, and may change
|
This testing framework is currently under development, and may change
|
||||||
@ -220,7 +220,7 @@ can be invoked on the ``Client`` instance.
|
|||||||
|
|
||||||
will result in the evaluation of a GET request equivalent to::
|
will result in the evaluation of a GET request equivalent to::
|
||||||
|
|
||||||
http://yoursite.com/customers/details/?name='fred'&age=7
|
http://yoursite.com/customers/details/?name=fred&age=7
|
||||||
|
|
||||||
``post(path, data={})``
|
``post(path, data={})``
|
||||||
Make a POST request on the provided ``path``. The key-value pairs in the
|
Make a POST request on the provided ``path``. The key-value pairs in the
|
||||||
@ -244,11 +244,11 @@ can be invoked on the ``Client`` instance.
|
|||||||
|
|
||||||
``login(path, username, password)``
|
``login(path, username, password)``
|
||||||
In a production site, it is likely that some views will be protected with
|
In a production site, it is likely that some views will be protected with
|
||||||
the @login_required URL provided by ``django.contrib.auth``. Interacting
|
the @login_required decorator provided by ``django.contrib.auth``. Interacting
|
||||||
with a URL that has been login protected is a slightly complex operation,
|
with a URL that has been login protected is a slightly complex operation,
|
||||||
so the Test Client provides a simple URL to automate the login process. A
|
so the Test Client provides a simple method to automate the login process. A
|
||||||
call to ``login()`` stimulates the series of GET and POST calls required
|
call to ``login()`` stimulates the series of GET and POST calls required
|
||||||
to log a user into a @login_required protected URL.
|
to log a user into a @login_required protected view.
|
||||||
|
|
||||||
If login is possible, the final return value of ``login()`` is the response
|
If login is possible, the final return value of ``login()`` is the response
|
||||||
that is generated by issuing a GET request on the protected URL. If login
|
that is generated by issuing a GET request on the protected URL. If login
|
||||||
@ -415,7 +415,7 @@ arguments:
|
|||||||
tested. This is the same format returned by ``django.db.models.get_apps()``
|
tested. This is the same format returned by ``django.db.models.get_apps()``
|
||||||
|
|
||||||
Verbosity determines the amount of notification and debug information that
|
Verbosity determines the amount of notification and debug information that
|
||||||
will be printed to the console; '0' is no output, '1' is normal output,
|
will be printed to the console; `0` is no output, `1` is normal output,
|
||||||
and `2` is verbose output.
|
and `2` is verbose output.
|
||||||
|
|
||||||
Testing utilities
|
Testing utilities
|
||||||
|
@ -17,7 +17,7 @@ installed by running the Python interactive interpreter and typing
|
|||||||
``import django``. If that command runs successfully, with no errors, Django is
|
``import django``. If that command runs successfully, with no errors, Django is
|
||||||
installed.
|
installed.
|
||||||
|
|
||||||
.. _`Django installed`: http://www.djangoproject.com/documentation/install/
|
.. _`Django installed`: ../install/
|
||||||
|
|
||||||
Creating a project
|
Creating a project
|
||||||
==================
|
==================
|
||||||
@ -108,7 +108,7 @@ It worked!
|
|||||||
|
|
||||||
Full docs for the development server are at `django-admin documentation`_.
|
Full docs for the development server are at `django-admin documentation`_.
|
||||||
|
|
||||||
.. _django-admin documentation: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin documentation: ../django_admin/
|
||||||
|
|
||||||
Database setup
|
Database setup
|
||||||
--------------
|
--------------
|
||||||
@ -378,7 +378,7 @@ as you like, and it will only ever create the tables that don't exist.
|
|||||||
Read the `django-admin.py documentation`_ for full information on what the
|
Read the `django-admin.py documentation`_ for full information on what the
|
||||||
``manage.py`` utility can do.
|
``manage.py`` utility can do.
|
||||||
|
|
||||||
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin.py documentation: ../django_admin/
|
||||||
|
|
||||||
Playing with the API
|
Playing with the API
|
||||||
====================
|
====================
|
||||||
@ -555,5 +555,5 @@ For full details on the database API, see our `Database API reference`_.
|
|||||||
When you're comfortable with the API, read `part 2 of this tutorial`_ to get
|
When you're comfortable with the API, read `part 2 of this tutorial`_ to get
|
||||||
Django's automatic admin working.
|
Django's automatic admin working.
|
||||||
|
|
||||||
.. _Database API reference: http://www.djangoproject.com/documentation/db_api/
|
.. _Database API reference: ../db_api/
|
||||||
.. _part 2 of this tutorial: http://www.djangoproject.com/documentation/tutorial2/
|
.. _part 2 of this tutorial: ../tutorial2/
|
||||||
|
@ -5,7 +5,7 @@ Writing your first Django app, part 2
|
|||||||
This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll
|
This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll
|
||||||
application and will focus on Django's automatically-generated admin site.
|
application and will focus on Django's automatically-generated admin site.
|
||||||
|
|
||||||
.. _Tutorial 1: http://www.djangoproject.com/documentation/tutorial1/
|
.. _Tutorial 1: ../tutorial1/
|
||||||
|
|
||||||
.. admonition:: Philosophy
|
.. admonition:: Philosophy
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ tutorial, remember?) You should see the Django admin index page:
|
|||||||
By default, you should see two types of editable content: groups and users.
|
By default, you should see two types of editable content: groups and users.
|
||||||
These are core features Django ships with by default.
|
These are core features Django ships with by default.
|
||||||
|
|
||||||
.. _"I can't log in" questions: http://www.djangoproject.com/documentation/faq/#the-admin-site
|
.. _"I can't log in" questions: ../faq/#the-admin-site
|
||||||
|
|
||||||
Make the poll app modifiable in the admin
|
Make the poll app modifiable in the admin
|
||||||
=========================================
|
=========================================
|
||||||
@ -402,7 +402,7 @@ Django automatically looks for a ``templates/`` subdirectory within each app
|
|||||||
package, for use as a fallback. See the `loader types documentation`_ for full
|
package, for use as a fallback. See the `loader types documentation`_ for full
|
||||||
information.
|
information.
|
||||||
|
|
||||||
.. _loader types documentation: http://www.djangoproject.com/documentation/templates_python/#loader-types
|
.. _loader types documentation: ../templates_python/#loader-types
|
||||||
|
|
||||||
Customize the admin index page
|
Customize the admin index page
|
||||||
==============================
|
==============================
|
||||||
@ -433,5 +433,5 @@ general, see the `Django admin CSS guide`_.
|
|||||||
When you're comfortable with the admin site, read `part 3 of this tutorial`_ to
|
When you're comfortable with the admin site, read `part 3 of this tutorial`_ to
|
||||||
start working on public poll views.
|
start working on public poll views.
|
||||||
|
|
||||||
.. _Django admin CSS guide: http://www.djangoproject.com/documentation/admin_css/
|
.. _Django admin CSS guide: ../admin_css/
|
||||||
.. _part 3 of this tutorial: http://www.djangoproject.com/documentation/tutorial3/
|
.. _part 3 of this tutorial: ../tutorial3/
|
||||||
|
@ -5,7 +5,7 @@ Writing your first Django app, part 3
|
|||||||
This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
|
This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
|
||||||
application and will focus on creating the public interface -- "views."
|
application and will focus on creating the public interface -- "views."
|
||||||
|
|
||||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
.. _Tutorial 2: ../tutorial2/
|
||||||
|
|
||||||
Philosophy
|
Philosophy
|
||||||
==========
|
==========
|
||||||
@ -117,8 +117,8 @@ time the URLconf module is loaded. They're super fast.
|
|||||||
|
|
||||||
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
|
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
|
||||||
.. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
|
.. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
|
||||||
.. _request and response documentation: http://www.djangoproject.com/documentation/request_response/
|
.. _request and response documentation: ../request_response/
|
||||||
.. _URLconf documentation: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf documentation: ../url_dispatch/
|
||||||
|
|
||||||
Write your first view
|
Write your first view
|
||||||
=====================
|
=====================
|
||||||
@ -260,8 +260,7 @@ provides a shortcut. Here's the full ``index()`` view, rewritten::
|
|||||||
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
|
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
|
||||||
return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
|
return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
|
||||||
|
|
||||||
Note that we no longer need to import ``loader``, ``Context`` or
|
Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
|
||||||
``HttpResponse``.
|
|
||||||
|
|
||||||
The ``render_to_response()`` function takes a template name as its first
|
The ``render_to_response()`` function takes a template name as its first
|
||||||
argument and a dictionary as its optional second argument. It returns an
|
argument and a dictionary as its optional second argument. It returns an
|
||||||
@ -300,7 +299,7 @@ rewritten::
|
|||||||
|
|
||||||
The ``get_object_or_404()`` function takes a Django model module as its first
|
The ``get_object_or_404()`` function takes a Django model module as its first
|
||||||
argument and an arbitrary number of keyword arguments, which it passes to the
|
argument and an arbitrary number of keyword arguments, which it passes to the
|
||||||
module's ``get_object()`` function. It raises ``Http404`` if the object doesn't
|
module's ``get()`` function. It raises ``Http404`` if the object doesn't
|
||||||
exist.
|
exist.
|
||||||
|
|
||||||
.. admonition:: Philosophy
|
.. admonition:: Philosophy
|
||||||
@ -377,7 +376,7 @@ iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
|
|||||||
|
|
||||||
See the `template guide`_ for full details on how templates work.
|
See the `template guide`_ for full details on how templates work.
|
||||||
|
|
||||||
.. _template guide: http://www.djangoproject.com/documentation/templates/
|
.. _template guide: ../templates/
|
||||||
|
|
||||||
Simplifying the URLconfs
|
Simplifying the URLconfs
|
||||||
========================
|
========================
|
||||||
@ -464,4 +463,4 @@ All the poll app cares about is its relative URLs, not its absolute URLs.
|
|||||||
When you're comfortable with writing views, read `part 4 of this tutorial`_ to
|
When you're comfortable with writing views, read `part 4 of this tutorial`_ to
|
||||||
learn about simple form processing and generic views.
|
learn about simple form processing and generic views.
|
||||||
|
|
||||||
.. _part 4 of this tutorial: http://www.djangoproject.com/documentation/tutorial4/
|
.. _part 4 of this tutorial: ../tutorial4/
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user