mirror of
https://github.com/django/django.git
synced 2025-07-05 18:29:11 +00:00
generic-auth: Merged to trunk [4254]
git-svn-id: http://code.djangoproject.com/svn/django/branches/generic-auth@4255 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
d87c354b65
commit
a8ba8485de
3
AUTHORS
3
AUTHORS
@ -118,10 +118,12 @@ answer newbie questions, and generally made Django that much better:
|
||||
Manuzhai
|
||||
Petar Marić
|
||||
mark@junklight.com
|
||||
Yasushi Masuda <whosaysni@gmail.com>
|
||||
mattycakes@gmail.com
|
||||
Jason McBrayer <http://www.carcosa.net/jason/>
|
||||
mccutchen@gmail.com
|
||||
michael.mcewan@gmail.com
|
||||
mitakummaa@gmail.com
|
||||
mmarshall
|
||||
Eric Moritz <http://eric.themoritzfamily.com/>
|
||||
Robin Munn <http://www.geekforgod.com/>
|
||||
@ -160,6 +162,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Tom Insam
|
||||
Joe Topjian <http://joe.terrarum.net/geek/code/python/django/>
|
||||
Karen Tracey <graybark@bellsouth.net>
|
||||
Makoto Tsuyuki <mtsuyuki@gmail.com>
|
||||
Amit Upadhyay
|
||||
Geert Vanderkelen
|
||||
Milton Waddams
|
||||
|
@ -25,7 +25,7 @@ ADMINS = ()
|
||||
INTERNAL_IPS = ()
|
||||
|
||||
# Local time zone for this installation. All choices can be found here:
|
||||
# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||
# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||
TIME_ZONE = 'America/Chicago'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
|
@ -17,7 +17,7 @@ DATABASE_HOST = '' # Set to empty string for localhost. Not used wit
|
||||
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
||||
|
||||
# Local time zone for this installation. All choices can be found here:
|
||||
# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||
# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||
TIME_ZONE = 'America/Chicago'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
|
@ -38,7 +38,10 @@
|
||||
<div id="content" class="{% block coltype %}colM{% endblock %}">
|
||||
{% block pretitle %}{% endblock %}
|
||||
{% block content_title %}{% if title %}<h1>{{ title|escape }}</h1>{% endif %}{% endblock %}
|
||||
{% block content %}{{ content }}{% endblock %}
|
||||
{% block content %}
|
||||
{% block object-tools %}{% endblock %}
|
||||
{{ content }}
|
||||
{% endblock %}
|
||||
{% block sidebar %}{% endblock %}
|
||||
<br class="clear" />
|
||||
</div>
|
||||
|
@ -16,11 +16,13 @@
|
||||
</div>
|
||||
{% endif %}{% endblock %}
|
||||
{% block content %}<div id="content-main">
|
||||
{% block object-tools %}
|
||||
{% if change %}{% if not is_popup %}
|
||||
<ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
|
||||
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
||||
</ul>
|
||||
{% endif %}{% endif %}
|
||||
{% endblock %}
|
||||
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
|
||||
<div>
|
||||
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
|
||||
|
@ -7,9 +7,11 @@
|
||||
{% block coltype %}flex{% endblock %}
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
{% block object-tools %}
|
||||
{% if has_add_permission %}
|
||||
<ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
|
||||
{% block search %}{% search_form cl %}{% endblock %}
|
||||
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}
|
||||
|
@ -2,7 +2,7 @@ from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
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.http import HttpResponseRedirect
|
||||
|
||||
@ -24,7 +24,7 @@ def user_add_stage(request):
|
||||
return HttpResponseRedirect('../%s/' % new_user.id)
|
||||
else:
|
||||
errors = new_data = {}
|
||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
return render_to_response('admin/auth/user/add_form.html', {
|
||||
'title': _('Add user'),
|
||||
'form': form,
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django import forms, template
|
||||
from django import oldforms, template
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import has_permission
|
||||
from django.contrib.admin.filterspecs import FilterSpec
|
||||
@ -288,7 +288,7 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po
|
||||
errors = {}
|
||||
|
||||
# Populate the FormWrapper.
|
||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
|
||||
c = template.RequestContext(request, {
|
||||
'title': _('Add %s') % opts.verbose_name,
|
||||
@ -379,7 +379,7 @@ def change_stage(request, app_label, model_name, object_id):
|
||||
errors = {}
|
||||
|
||||
# Populate the FormWrapper.
|
||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
form.original = manipulator.original_object
|
||||
form.order_objects = []
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.core import validators
|
||||
from django import template, forms
|
||||
from django import template, oldforms
|
||||
from django.template import loader
|
||||
from django.shortcuts import render_to_response
|
||||
from django.contrib.sites.models import Site
|
||||
@ -25,17 +25,17 @@ def template_validator(request):
|
||||
request.user.message_set.create(message='The template is valid.')
|
||||
return render_to_response('admin/template_validator.html', {
|
||||
'title': 'Template validator',
|
||||
'form': forms.FormWrapper(manipulator, new_data, errors),
|
||||
'form': oldforms.FormWrapper(manipulator, new_data, errors),
|
||||
}, context_instance=template.RequestContext(request))
|
||||
template_validator = staff_member_required(template_validator)
|
||||
|
||||
class TemplateValidator(forms.Manipulator):
|
||||
class TemplateValidator(oldforms.Manipulator):
|
||||
def __init__(self, settings_modules):
|
||||
self.settings_modules = settings_modules
|
||||
site_list = Site.objects.in_bulk(settings_modules.keys()).values()
|
||||
self.fields = (
|
||||
forms.SelectField('site', is_required=True, choices=[(s.id, s.name) for s in site_list]),
|
||||
forms.LargeTextField('template', is_required=True, rows=25, validator_list=[self.isValidTemplate]),
|
||||
oldforms.SelectField('site', is_required=True, choices=[(s.id, s.name) for s in site_list]),
|
||||
oldforms.LargeTextField('template', is_required=True, rows=25, validator_list=[self.isValidTemplate]),
|
||||
)
|
||||
|
||||
def isValidTemplate(self, field_data, all_data):
|
||||
|
@ -3,16 +3,16 @@ from django.contrib.auth import authenticate
|
||||
from django.contrib.sites.models import Site
|
||||
from django.template import Context, loader
|
||||
from django.core import validators
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
|
||||
class UserCreationForm(forms.Manipulator):
|
||||
class UserCreationForm(oldforms.Manipulator):
|
||||
"A form that creates a user, with no privileges, from the given username and password."
|
||||
def __init__(self):
|
||||
self.fields = (
|
||||
forms.TextField(field_name='username', length=30, maxlength=30, is_required=True,
|
||||
oldforms.TextField(field_name='username', length=30, maxlength=30, is_required=True,
|
||||
validator_list=[validators.isAlphaNumeric, self.isValidUsername]),
|
||||
forms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
|
||||
forms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
|
||||
oldforms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
|
||||
oldforms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
|
||||
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
|
||||
)
|
||||
|
||||
@ -27,7 +27,7 @@ class UserCreationForm(forms.Manipulator):
|
||||
"Creates the user."
|
||||
return User.objects.create_user(new_data['username'], '', new_data['password1'])
|
||||
|
||||
class AuthenticationForm(forms.Manipulator):
|
||||
class AuthenticationForm(oldforms.Manipulator):
|
||||
"""
|
||||
Base class for authenticating users. Extend this to get a form that accepts
|
||||
username/password logins.
|
||||
@ -41,9 +41,9 @@ class AuthenticationForm(forms.Manipulator):
|
||||
"""
|
||||
self.request = request
|
||||
self.fields = [
|
||||
forms.TextField(field_name="username", length=15, maxlength=30, is_required=True,
|
||||
oldforms.TextField(field_name="username", length=15, maxlength=30, is_required=True,
|
||||
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
|
||||
forms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True),
|
||||
oldforms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True),
|
||||
]
|
||||
self.user_cache = None
|
||||
|
||||
@ -68,11 +68,11 @@ class AuthenticationForm(forms.Manipulator):
|
||||
def get_user(self):
|
||||
return self.user_cache
|
||||
|
||||
class PasswordResetForm(forms.Manipulator):
|
||||
class PasswordResetForm(oldforms.Manipulator):
|
||||
"A form that lets a user request a password reset"
|
||||
def __init__(self):
|
||||
self.fields = (
|
||||
forms.EmailField(field_name="email", length=40, is_required=True,
|
||||
oldforms.EmailField(field_name="email", length=40, is_required=True,
|
||||
validator_list=[self.isValidUserEmail]),
|
||||
)
|
||||
|
||||
@ -105,16 +105,16 @@ class PasswordResetForm(forms.Manipulator):
|
||||
}
|
||||
send_mail('Password reset on %s' % site_name, t.render(Context(c)), None, [self.user_cache.email])
|
||||
|
||||
class PasswordChangeForm(forms.Manipulator):
|
||||
class PasswordChangeForm(oldforms.Manipulator):
|
||||
"A form that lets a user change his password."
|
||||
def __init__(self, user):
|
||||
self.user = user
|
||||
self.fields = (
|
||||
forms.PasswordField(field_name="old_password", length=30, maxlength=30, is_required=True,
|
||||
oldforms.PasswordField(field_name="old_password", length=30, maxlength=30, is_required=True,
|
||||
validator_list=[self.isValidOldPassword]),
|
||||
forms.PasswordField(field_name="new_password1", length=30, maxlength=30, is_required=True,
|
||||
oldforms.PasswordField(field_name="new_password1", length=30, maxlength=30, is_required=True,
|
||||
validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]),
|
||||
forms.PasswordField(field_name="new_password2", length=30, maxlength=30, is_required=True),
|
||||
oldforms.PasswordField(field_name="new_password2", length=30, maxlength=30, is_required=True),
|
||||
)
|
||||
|
||||
def isValidOldPassword(self, new_data, all_data):
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.contrib.sites.models import Site
|
||||
@ -26,7 +26,7 @@ def login(request, template_name='registration/login.html'):
|
||||
errors = {}
|
||||
request.session.set_test_cookie()
|
||||
return render_to_response(template_name, {
|
||||
'form': forms.FormWrapper(manipulator, request.POST, errors),
|
||||
'form': oldforms.FormWrapper(manipulator, request.POST, errors),
|
||||
REDIRECT_FIELD_NAME: redirect_to,
|
||||
'site_name': Site.objects.get_current().name,
|
||||
}, context_instance=RequestContext(request))
|
||||
@ -62,7 +62,7 @@ def password_reset(request, is_admin_site=False, template_name='registration/pas
|
||||
else:
|
||||
form.save(email_template_name=email_template_name)
|
||||
return HttpResponseRedirect('%sdone/' % request.path)
|
||||
return render_to_response(template_name, {'form': forms.FormWrapper(form, new_data, errors)},
|
||||
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def password_reset_done(request, template_name='registration/password_reset_done.html'):
|
||||
@ -77,7 +77,7 @@ def password_change(request, template_name='registration/password_change_form.ht
|
||||
if not errors:
|
||||
form.save(new_data)
|
||||
return HttpResponseRedirect('%sdone/' % request.path)
|
||||
return render_to_response(template_name, {'form': forms.FormWrapper(form, new_data, errors)},
|
||||
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
|
||||
context_instance=RequestContext(request))
|
||||
password_change = login_required(password_change)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
from django.core import validators
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django.core.mail import mail_admins, mail_managers
|
||||
from django.http import Http404
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
@ -28,37 +28,37 @@ class PublicCommentManipulator(AuthenticationForm):
|
||||
else:
|
||||
return []
|
||||
self.fields.extend([
|
||||
forms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
||||
oldforms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
||||
validator_list=[self.hasNoProfanities]),
|
||||
forms.RadioSelectField(field_name="rating1", choices=choices,
|
||||
oldforms.RadioSelectField(field_name="rating1", choices=choices,
|
||||
is_required=ratings_required and num_rating_choices > 0,
|
||||
validator_list=get_validator_list(1),
|
||||
),
|
||||
forms.RadioSelectField(field_name="rating2", choices=choices,
|
||||
oldforms.RadioSelectField(field_name="rating2", choices=choices,
|
||||
is_required=ratings_required and num_rating_choices > 1,
|
||||
validator_list=get_validator_list(2),
|
||||
),
|
||||
forms.RadioSelectField(field_name="rating3", choices=choices,
|
||||
oldforms.RadioSelectField(field_name="rating3", choices=choices,
|
||||
is_required=ratings_required and num_rating_choices > 2,
|
||||
validator_list=get_validator_list(3),
|
||||
),
|
||||
forms.RadioSelectField(field_name="rating4", choices=choices,
|
||||
oldforms.RadioSelectField(field_name="rating4", choices=choices,
|
||||
is_required=ratings_required and num_rating_choices > 3,
|
||||
validator_list=get_validator_list(4),
|
||||
),
|
||||
forms.RadioSelectField(field_name="rating5", choices=choices,
|
||||
oldforms.RadioSelectField(field_name="rating5", choices=choices,
|
||||
is_required=ratings_required and num_rating_choices > 4,
|
||||
validator_list=get_validator_list(5),
|
||||
),
|
||||
forms.RadioSelectField(field_name="rating6", choices=choices,
|
||||
oldforms.RadioSelectField(field_name="rating6", choices=choices,
|
||||
is_required=ratings_required and num_rating_choices > 5,
|
||||
validator_list=get_validator_list(6),
|
||||
),
|
||||
forms.RadioSelectField(field_name="rating7", choices=choices,
|
||||
oldforms.RadioSelectField(field_name="rating7", choices=choices,
|
||||
is_required=ratings_required and num_rating_choices > 6,
|
||||
validator_list=get_validator_list(7),
|
||||
),
|
||||
forms.RadioSelectField(field_name="rating8", choices=choices,
|
||||
oldforms.RadioSelectField(field_name="rating8", choices=choices,
|
||||
is_required=ratings_required and num_rating_choices > 7,
|
||||
validator_list=get_validator_list(8),
|
||||
),
|
||||
@ -117,13 +117,13 @@ class PublicCommentManipulator(AuthenticationForm):
|
||||
mail_managers("Comment posted by sketchy user (%s)" % self.user_cache.username, c.get_as_text())
|
||||
return c
|
||||
|
||||
class PublicFreeCommentManipulator(forms.Manipulator):
|
||||
class PublicFreeCommentManipulator(oldforms.Manipulator):
|
||||
"Manipulator that handles public free (unregistered) comments"
|
||||
def __init__(self):
|
||||
self.fields = (
|
||||
forms.TextField(field_name="person_name", maxlength=50, is_required=True,
|
||||
oldforms.TextField(field_name="person_name", maxlength=50, is_required=True,
|
||||
validator_list=[self.hasNoProfanities]),
|
||||
forms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
||||
oldforms.LargeTextField(field_name="comment", maxlength=3000, is_required=True,
|
||||
validator_list=[self.hasNoProfanities]),
|
||||
)
|
||||
|
||||
@ -221,9 +221,9 @@ def post_comment(request):
|
||||
from django.contrib.auth import login
|
||||
login(request, manipulator.get_user())
|
||||
if errors or request.POST.has_key('preview'):
|
||||
class CommentFormWrapper(forms.FormWrapper):
|
||||
class CommentFormWrapper(oldforms.FormWrapper):
|
||||
def __init__(self, manipulator, new_data, errors, rating_choices):
|
||||
forms.FormWrapper.__init__(self, manipulator, new_data, errors)
|
||||
oldforms.FormWrapper.__init__(self, manipulator, new_data, errors)
|
||||
self.rating_choices = rating_choices
|
||||
def ratings(self):
|
||||
field_list = [self['rating%d' % (i+1)] for i in range(len(rating_choices))]
|
||||
@ -302,7 +302,7 @@ def post_free_comment(request):
|
||||
comment = errors and '' or manipulator.get_comment(new_data)
|
||||
return render_to_response('comments/free_preview.html', {
|
||||
'comment': comment,
|
||||
'comment_form': forms.FormWrapper(manipulator, new_data, errors),
|
||||
'comment_form': oldforms.FormWrapper(manipulator, new_data, errors),
|
||||
'options': options,
|
||||
'target': target,
|
||||
'hash': security_hash,
|
||||
|
@ -11,7 +11,7 @@ import md5
|
||||
import re
|
||||
import itertools
|
||||
|
||||
_ERROR_MSG = "<h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p>"
|
||||
_ERROR_MSG = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>'
|
||||
|
||||
_POST_FORM_RE = \
|
||||
re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
|
||||
|
@ -60,7 +60,10 @@ class BaseHandler(object):
|
||||
if response:
|
||||
return response
|
||||
|
||||
resolver = urlresolvers.RegexURLResolver(r'^/', settings.ROOT_URLCONF)
|
||||
# Get urlconf from request object, if available. Otherwise use default.
|
||||
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
|
||||
|
||||
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
|
||||
try:
|
||||
callback, callback_args, callback_kwargs = resolver.resolve(request.path)
|
||||
|
||||
|
@ -20,6 +20,38 @@ except ImportError:
|
||||
# Import copy of _thread_local.py from Python 2.4
|
||||
from django.utils._threading_local import local
|
||||
|
||||
def smart_basestring(s, charset):
|
||||
if isinstance(s, unicode):
|
||||
return s.encode(charset)
|
||||
return s
|
||||
|
||||
class UnicodeCursorWrapper(object):
|
||||
"""
|
||||
A thin wrapper around psycopg cursors that allows them to accept Unicode
|
||||
strings as params.
|
||||
|
||||
This is necessary because psycopg doesn't apply any DB quoting to
|
||||
parameters that are Unicode strings. If a param is Unicode, this will
|
||||
convert it to a bytestring using DEFAULT_CHARSET before passing it to
|
||||
psycopg.
|
||||
"""
|
||||
def __init__(self, cursor, charset):
|
||||
self.cursor = cursor
|
||||
self.charset = charset
|
||||
|
||||
def execute(self, sql, params=()):
|
||||
return self.cursor.execute(sql, [smart_basestring(p, self.charset) for p in params])
|
||||
|
||||
def executemany(self, sql, param_list):
|
||||
new_param_list = [[smart_basestring(p, self.charset) for p in params] for params in param_list]
|
||||
return self.cursor.executemany(sql, new_param_list)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if self.__dict__.has_key(attr):
|
||||
return self.__dict__[attr]
|
||||
else:
|
||||
return getattr(self.cursor, attr)
|
||||
|
||||
class DatabaseWrapper(local):
|
||||
def __init__(self, **kwargs):
|
||||
self.connection = None
|
||||
@ -45,6 +77,7 @@ class DatabaseWrapper(local):
|
||||
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
|
||||
cursor = self.connection.cursor()
|
||||
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
||||
cursor = UnicodeCursorWrapper(cursor, settings.DEFAULT_CHARSET)
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
return cursor
|
||||
@ -118,7 +151,7 @@ def get_pk_default_value():
|
||||
try:
|
||||
Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
|
||||
except AttributeError:
|
||||
raise Exception, "You appear to be using psycopg version 2, which isn't supported yet, because it's still in beta. Use psycopg version 1 instead: http://initd.org/projects/psycopg1"
|
||||
raise Exception, "You appear to be using psycopg version 2. Set your DATABASE_ENGINE to 'postgresql_psycopg2' instead of 'postgresql'."
|
||||
Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time))
|
||||
Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp))
|
||||
Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean))
|
||||
|
@ -2,7 +2,8 @@ from django.db.models import signals
|
||||
from django.dispatch import dispatcher
|
||||
from django.conf import settings
|
||||
from django.core import validators
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django import newforms as forms
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.functional import curry
|
||||
from django.utils.itercompat import tee
|
||||
@ -206,10 +207,10 @@ class Field(object):
|
||||
|
||||
if self.choices:
|
||||
if self.radio_admin:
|
||||
field_objs = [forms.RadioSelectField]
|
||||
field_objs = [oldforms.RadioSelectField]
|
||||
params['ul_class'] = get_ul_class(self.radio_admin)
|
||||
else:
|
||||
field_objs = [forms.SelectField]
|
||||
field_objs = [oldforms.SelectField]
|
||||
|
||||
params['choices'] = self.get_choices_default()
|
||||
else:
|
||||
@ -218,7 +219,7 @@ class Field(object):
|
||||
|
||||
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
|
||||
"""
|
||||
Returns a list of forms.FormField instances for this field. It
|
||||
Returns a list of oldforms.FormField instances for this field. It
|
||||
calculates the choices at runtime, not at compile time.
|
||||
|
||||
name_prefix is a prefix to prepend to the "field_name" argument.
|
||||
@ -333,6 +334,15 @@ class Field(object):
|
||||
return self._choices
|
||||
choices = property(_get_choices)
|
||||
|
||||
def formfield(self, initial=None):
|
||||
"Returns a django.newforms.Field instance for this database Field."
|
||||
# TODO: This is just a temporary default during development.
|
||||
return forms.CharField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
def value_from_object(self, obj):
|
||||
"Returns the value of this field in the given model instance."
|
||||
return getattr(obj, self.attname)
|
||||
|
||||
class AutoField(Field):
|
||||
empty_strings_allowed = False
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -354,7 +364,7 @@ class AutoField(Field):
|
||||
return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.HiddenField]
|
||||
return [oldforms.HiddenField]
|
||||
|
||||
def get_manipulator_new_data(self, new_data, rel=False):
|
||||
# Never going to be called
|
||||
@ -369,6 +379,9 @@ class AutoField(Field):
|
||||
super(AutoField, self).contribute_to_class(cls, name)
|
||||
cls._meta.has_auto_field = True
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return None
|
||||
|
||||
class BooleanField(Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['blank'] = True
|
||||
@ -381,11 +394,14 @@ class BooleanField(Field):
|
||||
raise validators.ValidationError, gettext("This value must be either True or False.")
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.CheckboxField]
|
||||
return [oldforms.CheckboxField]
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.BooleanField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class CharField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.TextField]
|
||||
return [oldforms.TextField]
|
||||
|
||||
def to_python(self, value):
|
||||
if isinstance(value, basestring):
|
||||
@ -397,10 +413,13 @@ class CharField(Field):
|
||||
raise validators.ValidationError, gettext_lazy("This field cannot be null.")
|
||||
return str(value)
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.CharField(max_length=self.maxlength, required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
# TODO: Maybe move this into contrib, because it's specialized.
|
||||
class CommaSeparatedIntegerField(CharField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.CommaSeparatedIntegerField]
|
||||
return [oldforms.CommaSeparatedIntegerField]
|
||||
|
||||
class DateField(Field):
|
||||
empty_strings_allowed = False
|
||||
@ -462,12 +481,15 @@ class DateField(Field):
|
||||
return Field.get_db_prep_save(self, value)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.DateField]
|
||||
return [oldforms.DateField]
|
||||
|
||||
def flatten_data(self, follow, obj = None):
|
||||
def flatten_data(self, follow, obj=None):
|
||||
val = self._get_val_from_obj(obj)
|
||||
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.DateField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class DateTimeField(DateField):
|
||||
def to_python(self, value):
|
||||
if isinstance(value, datetime.datetime):
|
||||
@ -503,7 +525,7 @@ class DateTimeField(DateField):
|
||||
return Field.get_db_prep_lookup(self, lookup_type, value)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.DateField, forms.TimeField]
|
||||
return [oldforms.DateField, oldforms.TimeField]
|
||||
|
||||
def get_manipulator_field_names(self, name_prefix):
|
||||
return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
|
||||
@ -526,6 +548,9 @@ class DateTimeField(DateField):
|
||||
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
|
||||
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.DateTimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class EmailField(CharField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['maxlength'] = 75
|
||||
@ -535,11 +560,14 @@ class EmailField(CharField):
|
||||
return "CharField"
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.EmailField]
|
||||
return [oldforms.EmailField]
|
||||
|
||||
def validate(self, field_data, all_data):
|
||||
validators.isValidEmail(field_data, all_data)
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.EmailField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class FileField(Field):
|
||||
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
|
||||
self.upload_to = upload_to
|
||||
@ -599,7 +627,7 @@ class FileField(Field):
|
||||
os.remove(file_name)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.FileUploadField, forms.HiddenField]
|
||||
return [oldforms.FileUploadField, oldforms.HiddenField]
|
||||
|
||||
def get_manipulator_field_names(self, name_prefix):
|
||||
return [name_prefix + self.name + '_file', name_prefix + self.name]
|
||||
@ -627,7 +655,7 @@ class FilePathField(Field):
|
||||
Field.__init__(self, verbose_name, name, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [curry(forms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
|
||||
return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
|
||||
|
||||
class FloatField(Field):
|
||||
empty_strings_allowed = False
|
||||
@ -636,7 +664,7 @@ class FloatField(Field):
|
||||
Field.__init__(self, verbose_name, name, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [curry(forms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
|
||||
return [curry(oldforms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
|
||||
|
||||
class ImageField(FileField):
|
||||
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
|
||||
@ -644,7 +672,7 @@ class ImageField(FileField):
|
||||
FileField.__init__(self, verbose_name, name, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.ImageUploadField, forms.HiddenField]
|
||||
return [oldforms.ImageUploadField, oldforms.HiddenField]
|
||||
|
||||
def contribute_to_class(self, cls, name):
|
||||
super(ImageField, self).contribute_to_class(cls, name)
|
||||
@ -670,7 +698,10 @@ class ImageField(FileField):
|
||||
class IntegerField(Field):
|
||||
empty_strings_allowed = False
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.IntegerField]
|
||||
return [oldforms.IntegerField]
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.IntegerField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class IPAddressField(Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -678,7 +709,7 @@ class IPAddressField(Field):
|
||||
Field.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.IPAddressField]
|
||||
return [oldforms.IPAddressField]
|
||||
|
||||
def validate(self, field_data, all_data):
|
||||
validators.isValidIPAddress4(field_data, None)
|
||||
@ -689,22 +720,22 @@ class NullBooleanField(Field):
|
||||
Field.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.NullBooleanField]
|
||||
return [oldforms.NullBooleanField]
|
||||
|
||||
class PhoneNumberField(IntegerField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.PhoneNumberField]
|
||||
return [oldforms.PhoneNumberField]
|
||||
|
||||
def validate(self, field_data, all_data):
|
||||
validators.isValidPhone(field_data, all_data)
|
||||
|
||||
class PositiveIntegerField(IntegerField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.PositiveIntegerField]
|
||||
return [oldforms.PositiveIntegerField]
|
||||
|
||||
class PositiveSmallIntegerField(IntegerField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.PositiveSmallIntegerField]
|
||||
return [oldforms.PositiveSmallIntegerField]
|
||||
|
||||
class SlugField(Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -716,15 +747,15 @@ class SlugField(Field):
|
||||
Field.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.TextField]
|
||||
return [oldforms.TextField]
|
||||
|
||||
class SmallIntegerField(IntegerField):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.SmallIntegerField]
|
||||
return [oldforms.SmallIntegerField]
|
||||
|
||||
class TextField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.LargeTextField]
|
||||
return [oldforms.LargeTextField]
|
||||
|
||||
class TimeField(Field):
|
||||
empty_strings_allowed = False
|
||||
@ -760,24 +791,31 @@ class TimeField(Field):
|
||||
return Field.get_db_prep_save(self, value)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.TimeField]
|
||||
return [oldforms.TimeField]
|
||||
|
||||
def flatten_data(self,follow, obj = None):
|
||||
val = self._get_val_from_obj(obj)
|
||||
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.TimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class URLField(Field):
|
||||
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
|
||||
if verify_exists:
|
||||
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
|
||||
self.verify_exists = verify_exists
|
||||
Field.__init__(self, verbose_name, name, **kwargs)
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.URLField]
|
||||
return [oldforms.URLField]
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.URLField(required=not self.blank, verify_exists=self.verify_exists, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class USStateField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [forms.USStateField]
|
||||
return [oldforms.USStateField]
|
||||
|
||||
class XMLField(TextField):
|
||||
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
|
||||
@ -788,7 +826,7 @@ class XMLField(TextField):
|
||||
return "TextField"
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
return [curry(forms.XMLLargeTextField, schema_path=self.schema_path)]
|
||||
return [curry(oldforms.XMLLargeTextField, schema_path=self.schema_path)]
|
||||
|
||||
class OrderingField(IntegerField):
|
||||
empty_strings_allowed=False
|
||||
@ -801,4 +839,4 @@ class OrderingField(IntegerField):
|
||||
return "IntegerField"
|
||||
|
||||
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
|
||||
return [forms.HiddenField(name_prefix + self.name)]
|
||||
return [oldforms.HiddenField(name_prefix + self.name)]
|
||||
|
@ -2,7 +2,7 @@
|
||||
Classes allowing "generic" relations through ContentType and object-id fields.
|
||||
"""
|
||||
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import backend
|
||||
from django.db.models import signals
|
||||
@ -98,7 +98,7 @@ class GenericRelation(RelatedField, Field):
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
choices = self.get_choices_default()
|
||||
return [curry(forms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
||||
return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
||||
|
||||
def get_choices_default(self):
|
||||
return Field.get_choices(self, include_blank=False)
|
||||
|
@ -2,10 +2,12 @@ from django.db import backend, transaction
|
||||
from django.db.models import signals, get_model
|
||||
from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class
|
||||
from django.db.models.related import RelatedObject
|
||||
from django.utils.text import capfirst
|
||||
from django.utils.translation import gettext_lazy, string_concat, ngettext
|
||||
from django.utils.functional import curry
|
||||
from django.core import validators
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django import newforms as forms
|
||||
from django.dispatch import dispatcher
|
||||
|
||||
# For Python 2.3
|
||||
@ -256,8 +258,7 @@ class ForeignRelatedObjectsDescriptor(object):
|
||||
# Otherwise, just move the named objects into the set.
|
||||
if self.related.field.null:
|
||||
manager.clear()
|
||||
for obj in value:
|
||||
manager.add(obj)
|
||||
manager.add(*value)
|
||||
|
||||
def create_many_related_manager(superclass):
|
||||
"""Creates a manager that subclasses 'superclass' (which is a Manager)
|
||||
@ -318,25 +319,31 @@ def create_many_related_manager(superclass):
|
||||
# *objs - objects to add
|
||||
from django.db import connection
|
||||
|
||||
# Add the newly created or already existing objects to the join table.
|
||||
# First find out which items are already added, to avoid adding them twice
|
||||
new_ids = set([obj._get_pk_val() for obj in objs])
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||
(target_col_name, self.join_table, source_col_name,
|
||||
target_col_name, ",".join(['%s'] * len(new_ids))),
|
||||
[self._pk_val] + list(new_ids))
|
||||
if cursor.rowcount is not None and cursor.rowcount != 0:
|
||||
existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)])
|
||||
else:
|
||||
existing_ids = set()
|
||||
# If there aren't any objects, there is nothing to do.
|
||||
if objs:
|
||||
# Check that all the objects are of the right type
|
||||
for obj in objs:
|
||||
if not isinstance(obj, self.model):
|
||||
raise ValueError, "objects to add() must be %s instances" % self.model._meta.object_name
|
||||
# Add the newly created or already existing objects to the join table.
|
||||
# First find out which items are already added, to avoid adding them twice
|
||||
new_ids = set([obj._get_pk_val() for obj in objs])
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||
(target_col_name, self.join_table, source_col_name,
|
||||
target_col_name, ",".join(['%s'] * len(new_ids))),
|
||||
[self._pk_val] + list(new_ids))
|
||||
if cursor.rowcount is not None and cursor.rowcount != 0:
|
||||
existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)])
|
||||
else:
|
||||
existing_ids = set()
|
||||
|
||||
# Add the ones that aren't there already
|
||||
for obj_id in (new_ids - existing_ids):
|
||||
cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
|
||||
(self.join_table, source_col_name, target_col_name),
|
||||
[self._pk_val, obj_id])
|
||||
transaction.commit_unless_managed()
|
||||
# Add the ones that aren't there already
|
||||
for obj_id in (new_ids - existing_ids):
|
||||
cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
|
||||
(self.join_table, source_col_name, target_col_name),
|
||||
[self._pk_val, obj_id])
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
def _remove_items(self, source_col_name, target_col_name, *objs):
|
||||
# source_col_name: the PK colname in join_table for the source object
|
||||
@ -344,16 +351,20 @@ def create_many_related_manager(superclass):
|
||||
# *objs - objects to remove
|
||||
from django.db import connection
|
||||
|
||||
for obj in objs:
|
||||
if not isinstance(obj, self.model):
|
||||
raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name
|
||||
# Remove the specified objects from the join table
|
||||
cursor = connection.cursor()
|
||||
for obj in objs:
|
||||
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \
|
||||
(self.join_table, source_col_name, target_col_name),
|
||||
[self._pk_val, obj._get_pk_val()])
|
||||
transaction.commit_unless_managed()
|
||||
# If there aren't any objects, there is nothing to do.
|
||||
if objs:
|
||||
# Check that all the objects are of the right type
|
||||
for obj in objs:
|
||||
if not isinstance(obj, self.model):
|
||||
raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name
|
||||
# Remove the specified objects from the join table
|
||||
old_ids = set([obj._get_pk_val() for obj in objs])
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||
(self.join_table, source_col_name,
|
||||
target_col_name, ",".join(['%s'] * len(old_ids))),
|
||||
[self._pk_val] + list(old_ids))
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
def _clear_items(self, source_col_name):
|
||||
# source_col_name: the PK colname in join_table for the source object
|
||||
@ -405,8 +416,7 @@ class ManyRelatedObjectsDescriptor(object):
|
||||
|
||||
manager = self.__get__(instance)
|
||||
manager.clear()
|
||||
for obj in value:
|
||||
manager.add(obj)
|
||||
manager.add(*value)
|
||||
|
||||
class ReverseManyRelatedObjectsDescriptor(object):
|
||||
# This class provides the functionality that makes the related-object
|
||||
@ -447,8 +457,7 @@ class ReverseManyRelatedObjectsDescriptor(object):
|
||||
|
||||
manager = self.__get__(instance)
|
||||
manager.clear()
|
||||
for obj in value:
|
||||
manager.add(obj)
|
||||
manager.add(*value)
|
||||
|
||||
class ForeignKey(RelatedField, Field):
|
||||
empty_strings_allowed = False
|
||||
@ -493,13 +502,13 @@ class ForeignKey(RelatedField, Field):
|
||||
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
|
||||
else:
|
||||
if self.radio_admin:
|
||||
field_objs = [forms.RadioSelectField]
|
||||
field_objs = [oldforms.RadioSelectField]
|
||||
params['ul_class'] = get_ul_class(self.radio_admin)
|
||||
else:
|
||||
if self.null:
|
||||
field_objs = [forms.NullSelectField]
|
||||
field_objs = [oldforms.NullSelectField]
|
||||
else:
|
||||
field_objs = [forms.SelectField]
|
||||
field_objs = [oldforms.SelectField]
|
||||
params['choices'] = self.get_choices_default()
|
||||
return field_objs, params
|
||||
|
||||
@ -508,7 +517,7 @@ class ForeignKey(RelatedField, Field):
|
||||
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
|
||||
return rel_field.get_manipulator_field_objs()
|
||||
else:
|
||||
return [forms.IntegerField]
|
||||
return [oldforms.IntegerField]
|
||||
|
||||
def get_db_prep_save(self, value):
|
||||
if value == '' or value == None:
|
||||
@ -539,6 +548,9 @@ class ForeignKey(RelatedField, Field):
|
||||
def contribute_to_related_class(self, cls, related):
|
||||
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class OneToOneField(RelatedField, IntegerField):
|
||||
def __init__(self, to, to_field=None, **kwargs):
|
||||
try:
|
||||
@ -581,13 +593,13 @@ class OneToOneField(RelatedField, IntegerField):
|
||||
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
|
||||
else:
|
||||
if self.radio_admin:
|
||||
field_objs = [forms.RadioSelectField]
|
||||
field_objs = [oldforms.RadioSelectField]
|
||||
params['ul_class'] = get_ul_class(self.radio_admin)
|
||||
else:
|
||||
if self.null:
|
||||
field_objs = [forms.NullSelectField]
|
||||
field_objs = [oldforms.NullSelectField]
|
||||
else:
|
||||
field_objs = [forms.SelectField]
|
||||
field_objs = [oldforms.SelectField]
|
||||
params['choices'] = self.get_choices_default()
|
||||
return field_objs, params
|
||||
|
||||
@ -600,6 +612,9 @@ class OneToOneField(RelatedField, IntegerField):
|
||||
if not cls._meta.one_to_one_field:
|
||||
cls._meta.one_to_one_field = self
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class ManyToManyField(RelatedField, Field):
|
||||
def __init__(self, to, **kwargs):
|
||||
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
|
||||
@ -622,10 +637,10 @@ class ManyToManyField(RelatedField, Field):
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
if self.rel.raw_id_admin:
|
||||
return [forms.RawIdAdminField]
|
||||
return [oldforms.RawIdAdminField]
|
||||
else:
|
||||
choices = self.get_choices_default()
|
||||
return [curry(forms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
||||
return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
||||
|
||||
def get_choices_default(self):
|
||||
return Field.get_choices(self, include_blank=False)
|
||||
@ -706,6 +721,13 @@ class ManyToManyField(RelatedField, Field):
|
||||
def set_attributes_from_rel(self):
|
||||
pass
|
||||
|
||||
def value_from_object(self, obj):
|
||||
"Returns the value of this field in the given model instance."
|
||||
return getattr(obj, self.attname).all()
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.MultipleChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
|
||||
class ManyToOneRel(object):
|
||||
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
||||
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
|
||||
|
@ -1,5 +1,5 @@
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django.core import validators
|
||||
from django.db.models.fields import FileField, AutoField
|
||||
from django.dispatch import dispatcher
|
||||
@ -40,7 +40,7 @@ class ManipulatorDescriptor(object):
|
||||
self.man._prepare(model)
|
||||
return self.man
|
||||
|
||||
class AutomaticManipulator(forms.Manipulator):
|
||||
class AutomaticManipulator(oldforms.Manipulator):
|
||||
def _prepare(cls, model):
|
||||
cls.model = model
|
||||
cls.manager = model._default_manager
|
||||
@ -76,7 +76,7 @@ class AutomaticManipulator(forms.Manipulator):
|
||||
|
||||
# Add field for ordering.
|
||||
if self.change and self.opts.get_ordered_objects():
|
||||
self.fields.append(forms.CommaSeparatedIntegerField(field_name="order_"))
|
||||
self.fields.append(oldforms.CommaSeparatedIntegerField(field_name="order_"))
|
||||
|
||||
def save(self, new_data):
|
||||
# TODO: big cleanup when core fields go -> use recursive manipulators.
|
||||
@ -308,7 +308,7 @@ def manipulator_validator_unique_together(field_name_list, opts, self, field_dat
|
||||
def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_type, self, field_data, all_data):
|
||||
from django.db.models.fields.related import ManyToOneRel
|
||||
date_str = all_data.get(date_field.get_manipulator_field_names('')[0], None)
|
||||
date_val = forms.DateField.html2python(date_str)
|
||||
date_val = oldforms.DateField.html2python(date_str)
|
||||
if date_val is None:
|
||||
return # Date was invalid. This will be caught by another validator.
|
||||
lookup_kwargs = {'%s__year' % date_field.name: date_val.year}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,5 +13,5 @@ TODO:
|
||||
from util import ValidationError
|
||||
from widgets import *
|
||||
from fields import *
|
||||
from forms import Form
|
||||
from forms import *
|
||||
from models import *
|
||||
|
1
django/newforms/extras/__init__.py
Normal file
1
django/newforms/extras/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from widgets import *
|
59
django/newforms/extras/widgets.py
Normal file
59
django/newforms/extras/widgets.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""
|
||||
Extra HTML Widget classes
|
||||
"""
|
||||
|
||||
from django.newforms.widgets import Widget, Select
|
||||
from django.utils.dates import MONTHS
|
||||
import datetime
|
||||
|
||||
__all__ = ('SelectDateWidget',)
|
||||
|
||||
class SelectDateWidget(Widget):
|
||||
"""
|
||||
A Widget that splits date input into three <select> boxes.
|
||||
|
||||
This also serves as an example of a Widget that has more than one HTML
|
||||
element and hence implements value_from_datadict.
|
||||
"""
|
||||
month_field = '%s_month'
|
||||
day_field = '%s_day'
|
||||
year_field = '%s_year'
|
||||
|
||||
def __init__(self, attrs=None, years=None):
|
||||
# years is an optional list/tuple of years to use in the "year" select box.
|
||||
self.attrs = attrs or {}
|
||||
if years:
|
||||
self.years = years
|
||||
else:
|
||||
this_year = datetime.date.today().year
|
||||
self.years = range(this_year, this_year+10)
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
try:
|
||||
value = datetime.date(*map(int, value.split('-')))
|
||||
year_val, month_val, day_val = value.year, value.month, value.day
|
||||
except (AttributeError, TypeError, ValueError):
|
||||
year_val = month_val = day_val = None
|
||||
|
||||
output = []
|
||||
|
||||
month_choices = MONTHS.items()
|
||||
month_choices.sort()
|
||||
select_html = Select(choices=month_choices).render(self.month_field % name, month_val)
|
||||
output.append(select_html)
|
||||
|
||||
day_choices = [(i, i) for i in range(1, 32)]
|
||||
select_html = Select(choices=day_choices).render(self.day_field % name, day_val)
|
||||
output.append(select_html)
|
||||
|
||||
year_choices = [(i, i) for i in self.years]
|
||||
select_html = Select(choices=year_choices).render(self.year_field % name, year_val)
|
||||
output.append(select_html)
|
||||
|
||||
return u'\n'.join(output)
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name)
|
||||
if y and m and d:
|
||||
return '%s-%s-%s' % (y, m, d)
|
||||
return None
|
@ -4,7 +4,7 @@ Field classes
|
||||
|
||||
from django.utils.translation import gettext
|
||||
from util import ValidationError, smart_unicode
|
||||
from widgets import TextInput, CheckboxInput, Select, SelectMultiple
|
||||
from widgets import TextInput, PasswordInput, CheckboxInput, Select, SelectMultiple
|
||||
import datetime
|
||||
import re
|
||||
import time
|
||||
@ -12,6 +12,7 @@ import time
|
||||
__all__ = (
|
||||
'Field', 'CharField', 'IntegerField',
|
||||
'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
|
||||
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
|
||||
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
|
||||
'RegexField', 'EmailField', 'URLField', 'BooleanField',
|
||||
'ChoiceField', 'MultipleChoiceField',
|
||||
@ -32,11 +33,30 @@ class Field(object):
|
||||
# Tracks each time a Field instance is created. Used to retain order.
|
||||
creation_counter = 0
|
||||
|
||||
def __init__(self, required=True, widget=None):
|
||||
self.required = required
|
||||
def __init__(self, required=True, widget=None, label=None, initial=None):
|
||||
# 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.
|
||||
if label is not None:
|
||||
label = smart_unicode(label)
|
||||
self.required, self.label, self.initial = required, label, initial
|
||||
widget = widget or self.widget
|
||||
if isinstance(widget, type):
|
||||
widget = widget()
|
||||
|
||||
# Hook into self.widget_attrs() for any Field-specific HTML attributes.
|
||||
extra_attrs = self.widget_attrs(widget)
|
||||
if extra_attrs:
|
||||
widget.attrs.update(extra_attrs)
|
||||
|
||||
self.widget = widget
|
||||
|
||||
# Increase the creation counter, and save our local copy.
|
||||
@ -54,15 +74,26 @@ class Field(object):
|
||||
raise ValidationError(gettext(u'This field is required.'))
|
||||
return value
|
||||
|
||||
def widget_attrs(self, widget):
|
||||
"""
|
||||
Given a Widget instance (*not* a Widget class), returns a dictionary of
|
||||
any HTML attributes that should be added to the Widget, based on this
|
||||
Field.
|
||||
"""
|
||||
return {}
|
||||
|
||||
class CharField(Field):
|
||||
def __init__(self, max_length=None, min_length=None, required=True, widget=None):
|
||||
Field.__init__(self, required, widget)
|
||||
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None, initial=None):
|
||||
self.max_length, self.min_length = max_length, min_length
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
|
||||
def clean(self, value):
|
||||
"Validates max_length and min_length. Returns a Unicode object."
|
||||
Field.clean(self, value)
|
||||
if value in EMPTY_VALUES: value = u''
|
||||
if value in EMPTY_VALUES:
|
||||
value = u''
|
||||
if not self.required:
|
||||
return value
|
||||
value = smart_unicode(value)
|
||||
if self.max_length is not None and len(value) > self.max_length:
|
||||
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||
@ -70,7 +101,15 @@ class CharField(Field):
|
||||
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
|
||||
return value
|
||||
|
||||
def widget_attrs(self, widget):
|
||||
if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
|
||||
return {'maxlength': str(self.max_length)}
|
||||
|
||||
class IntegerField(Field):
|
||||
def __init__(self, max_value=None, min_value=None, required=True, widget=None, label=None, initial=None):
|
||||
self.max_value, self.min_value = max_value, min_value
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Validates that int() can be called on the input. Returns the result
|
||||
@ -80,9 +119,14 @@ class IntegerField(Field):
|
||||
if not self.required and value in EMPTY_VALUES:
|
||||
return u''
|
||||
try:
|
||||
return int(value)
|
||||
value = int(value)
|
||||
except (ValueError, TypeError):
|
||||
raise ValidationError(gettext(u'Enter a whole number.'))
|
||||
if self.max_value is not None and value > self.max_value:
|
||||
raise ValidationError(gettext(u'Ensure this value is less than or equal to %s.') % self.max_value)
|
||||
if self.min_value is not None and value < self.min_value:
|
||||
raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value)
|
||||
return value
|
||||
|
||||
DEFAULT_DATE_INPUT_FORMATS = (
|
||||
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
|
||||
@ -93,8 +137,8 @@ DEFAULT_DATE_INPUT_FORMATS = (
|
||||
)
|
||||
|
||||
class DateField(Field):
|
||||
def __init__(self, input_formats=None, required=True, widget=None):
|
||||
Field.__init__(self, required, widget)
|
||||
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
||||
|
||||
def clean(self, value):
|
||||
@ -116,6 +160,33 @@ class DateField(Field):
|
||||
continue
|
||||
raise ValidationError(gettext(u'Enter a valid date.'))
|
||||
|
||||
DEFAULT_TIME_INPUT_FORMATS = (
|
||||
'%H:%M:%S', # '14:30:59'
|
||||
'%H:%M', # '14:30'
|
||||
)
|
||||
|
||||
class TimeField(Field):
|
||||
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Validates that the input can be converted to a time. Returns a Python
|
||||
datetime.time object.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
if value in EMPTY_VALUES:
|
||||
return None
|
||||
if isinstance(value, datetime.time):
|
||||
return value
|
||||
for format in self.input_formats:
|
||||
try:
|
||||
return datetime.time(*time.strptime(value, format)[3:6])
|
||||
except ValueError:
|
||||
continue
|
||||
raise ValidationError(gettext(u'Enter a valid time.'))
|
||||
|
||||
DEFAULT_DATETIME_INPUT_FORMATS = (
|
||||
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
|
||||
'%Y-%m-%d %H:%M', # '2006-10-25 14:30'
|
||||
@ -129,8 +200,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
|
||||
)
|
||||
|
||||
class DateTimeField(Field):
|
||||
def __init__(self, input_formats=None, required=True, widget=None):
|
||||
Field.__init__(self, required, widget)
|
||||
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
||||
|
||||
def clean(self, value):
|
||||
@ -153,16 +224,18 @@ class DateTimeField(Field):
|
||||
raise ValidationError(gettext(u'Enter a valid date/time.'))
|
||||
|
||||
class RegexField(Field):
|
||||
def __init__(self, regex, error_message=None, required=True, widget=None):
|
||||
def __init__(self, regex, max_length=None, min_length=None, error_message=None,
|
||||
required=True, widget=None, label=None, initial=None):
|
||||
"""
|
||||
regex can be either a string or a compiled regular expression object.
|
||||
error_message is an optional error message to use, if
|
||||
'Enter a valid value' is too generic for you.
|
||||
"""
|
||||
Field.__init__(self, required, widget)
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
if isinstance(regex, basestring):
|
||||
regex = re.compile(regex)
|
||||
self.regex = regex
|
||||
self.max_length, self.min_length = max_length, min_length
|
||||
self.error_message = error_message or gettext(u'Enter a valid value.')
|
||||
|
||||
def clean(self, value):
|
||||
@ -175,6 +248,10 @@ class RegexField(Field):
|
||||
value = smart_unicode(value)
|
||||
if not self.required and value == u'':
|
||||
return value
|
||||
if self.max_length is not None and len(value) > self.max_length:
|
||||
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||
if self.min_length is not None and len(value) < self.min_length:
|
||||
raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
|
||||
if not self.regex.search(value):
|
||||
raise ValidationError(self.error_message)
|
||||
return value
|
||||
@ -185,8 +262,8 @@ email_re = re.compile(
|
||||
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
|
||||
|
||||
class EmailField(RegexField):
|
||||
def __init__(self, required=True, widget=None):
|
||||
RegexField.__init__(self, email_re, gettext(u'Enter a valid e-mail address.'), required, widget)
|
||||
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None, initial=None):
|
||||
RegexField.__init__(self, email_re, max_length, min_length, gettext(u'Enter a valid e-mail address.'), required, widget, label, initial)
|
||||
|
||||
url_re = re.compile(
|
||||
r'^https?://' # http:// or https://
|
||||
@ -202,9 +279,9 @@ except ImportError:
|
||||
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
||||
|
||||
class URLField(RegexField):
|
||||
def __init__(self, required=True, verify_exists=False, widget=None,
|
||||
validator_user_agent=URL_VALIDATOR_USER_AGENT):
|
||||
RegexField.__init__(self, url_re, gettext(u'Enter a valid URL.'), required, widget)
|
||||
def __init__(self, max_length=None, min_length=None, required=True, verify_exists=False, widget=None, label=None,
|
||||
initial=None, validator_user_agent=URL_VALIDATOR_USER_AGENT):
|
||||
RegexField.__init__(self, url_re, max_length, min_length, gettext(u'Enter a valid URL.'), required, widget, label, initial)
|
||||
self.verify_exists = verify_exists
|
||||
self.user_agent = validator_user_agent
|
||||
|
||||
@ -238,10 +315,10 @@ class BooleanField(Field):
|
||||
return bool(value)
|
||||
|
||||
class ChoiceField(Field):
|
||||
def __init__(self, choices=(), required=True, widget=Select):
|
||||
def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None):
|
||||
if isinstance(widget, type):
|
||||
widget = widget(choices=choices)
|
||||
Field.__init__(self, required, widget)
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
self.choices = choices
|
||||
|
||||
def clean(self, value):
|
||||
@ -259,8 +336,8 @@ class ChoiceField(Field):
|
||||
return value
|
||||
|
||||
class MultipleChoiceField(ChoiceField):
|
||||
def __init__(self, choices=(), required=True, widget=SelectMultiple):
|
||||
ChoiceField.__init__(self, choices, required, widget)
|
||||
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None):
|
||||
ChoiceField.__init__(self, choices, required, widget, label, initial)
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
@ -277,15 +354,15 @@ class MultipleChoiceField(ChoiceField):
|
||||
val = smart_unicode(val)
|
||||
new_value.append(val)
|
||||
# Validate that each value in the value list is in self.choices.
|
||||
valid_values = set([k for k, v in self.choices])
|
||||
valid_values = set([smart_unicode(k) for k, v in self.choices])
|
||||
for val in new_value:
|
||||
if val not in valid_values:
|
||||
raise ValidationError(gettext(u'Select a valid choice. %s is not one of the available choices.') % val)
|
||||
return new_value
|
||||
|
||||
class ComboField(Field):
|
||||
def __init__(self, fields=(), required=True, widget=None):
|
||||
Field.__init__(self, required, widget)
|
||||
def __init__(self, fields=(), required=True, widget=None, label=None, initial=None):
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
# Set 'required' to False on the individual fields, because the
|
||||
# required validation will be handled by ComboField, not by those
|
||||
# individual fields.
|
||||
|
@ -2,12 +2,14 @@
|
||||
Form classes
|
||||
"""
|
||||
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.utils.datastructures import SortedDict, MultiValueDict
|
||||
from django.utils.html import escape
|
||||
from fields import Field
|
||||
from widgets import TextInput, Textarea, HiddenInput
|
||||
from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError
|
||||
|
||||
__all__ = ('BaseForm', 'Form')
|
||||
|
||||
NON_FIELD_ERRORS = '__all__'
|
||||
|
||||
def pretty_name(name):
|
||||
@ -32,14 +34,16 @@ class DeclarativeFieldsMetaclass(type):
|
||||
attrs['fields'] = SortedDictFromList(fields)
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
|
||||
class Form(StrAndUnicode):
|
||||
"A collection of Fields, plus their associated data."
|
||||
__metaclass__ = DeclarativeFieldsMetaclass
|
||||
|
||||
def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
|
||||
class BaseForm(StrAndUnicode):
|
||||
# This is the main implementation of all the Form logic. Note that this
|
||||
# class is different than Form. See the comments by the Form class for more
|
||||
# information. Any improvements to the form API should be made to *this*
|
||||
# class, not to the Form class.
|
||||
def __init__(self, data=None, auto_id='id_%s', prefix=None):
|
||||
self.ignore_errors = data is None
|
||||
self.data = data or {}
|
||||
self.auto_id = auto_id
|
||||
self.prefix = prefix
|
||||
self.clean_data = None # Stores the data after clean() has been called.
|
||||
self.__errors = None # Stores the errors after clean() has been called.
|
||||
|
||||
@ -72,6 +76,15 @@ class Form(StrAndUnicode):
|
||||
"""
|
||||
return not self.ignore_errors and not bool(self.errors)
|
||||
|
||||
def add_prefix(self, field_name):
|
||||
"""
|
||||
Returns the field name with a prefix appended, if this Form has a
|
||||
prefix set.
|
||||
|
||||
Subclasses may wish to override.
|
||||
"""
|
||||
return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name
|
||||
|
||||
def _html_output(self, normal_row, error_row, row_ender, errors_on_separate_row):
|
||||
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
|
||||
top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
|
||||
@ -86,7 +99,8 @@ class Form(StrAndUnicode):
|
||||
else:
|
||||
if errors_on_separate_row and bf_errors:
|
||||
output.append(error_row % bf_errors)
|
||||
output.append(normal_row % {'errors': bf_errors, 'label': bf.label_tag(escape(bf.verbose_name+':')), 'field': bf})
|
||||
label = bf.label and bf.label_tag(escape(bf.label + ':')) or ''
|
||||
output.append(normal_row % {'errors': bf_errors, 'label': label, 'field': bf})
|
||||
if top_errors:
|
||||
output.insert(0, error_row % top_errors)
|
||||
if hidden_fields: # Insert any hidden fields in the last row.
|
||||
@ -101,7 +115,7 @@ class Form(StrAndUnicode):
|
||||
|
||||
def as_table(self):
|
||||
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
|
||||
return self._html_output(u'<tr><td>%(label)s</td><td>%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', True)
|
||||
return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', False)
|
||||
|
||||
def as_ul(self):
|
||||
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
|
||||
@ -132,7 +146,7 @@ class Form(StrAndUnicode):
|
||||
# value_from_datadict() gets the data from the dictionary.
|
||||
# Each widget type knows how to retrieve its own data, because some
|
||||
# widgets split data over several HTML fields.
|
||||
value = field.widget.value_from_datadict(self.data, name)
|
||||
value = field.widget.value_from_datadict(self.data, self.add_prefix(name))
|
||||
try:
|
||||
value = field.clean(value)
|
||||
self.clean_data[name] = value
|
||||
@ -158,12 +172,26 @@ class Form(StrAndUnicode):
|
||||
"""
|
||||
return self.clean_data
|
||||
|
||||
class Form(BaseForm):
|
||||
"A collection of Fields, plus their associated data."
|
||||
# This is a separate class from BaseForm in order to abstract the way
|
||||
# self.fields is specified. This class (Form) is the one that does the
|
||||
# fancy metaclass stuff purely for the semantic sugar -- it allows one
|
||||
# to define a form using declarative syntax.
|
||||
# BaseForm itself has no way of designating self.fields.
|
||||
__metaclass__ = DeclarativeFieldsMetaclass
|
||||
|
||||
class BoundField(StrAndUnicode):
|
||||
"A Field plus data"
|
||||
def __init__(self, form, field, name):
|
||||
self.form = form
|
||||
self.field = field
|
||||
self.name = name
|
||||
self.html_name = form.add_prefix(name)
|
||||
if self.field.label is None:
|
||||
self.label = pretty_name(name)
|
||||
else:
|
||||
self.label = self.field.label
|
||||
|
||||
def __unicode__(self):
|
||||
"Renders this field as an HTML widget."
|
||||
@ -190,7 +218,11 @@ class BoundField(StrAndUnicode):
|
||||
auto_id = self.auto_id
|
||||
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
|
||||
attrs['id'] = auto_id
|
||||
return widget.render(self.name, self.data, attrs=attrs)
|
||||
if self.form.ignore_errors:
|
||||
data = self.field.initial
|
||||
else:
|
||||
data = self.data
|
||||
return widget.render(self.html_name, data, attrs=attrs)
|
||||
|
||||
def as_text(self, attrs=None):
|
||||
"""
|
||||
@ -209,21 +241,19 @@ class BoundField(StrAndUnicode):
|
||||
return self.as_widget(HiddenInput(), attrs)
|
||||
|
||||
def _data(self):
|
||||
"Returns the data for this BoundField, or None if it wasn't given."
|
||||
return self.form.data.get(self.name, None)
|
||||
"""
|
||||
Returns the data for this BoundField, or None if it wasn't given.
|
||||
"""
|
||||
return self.field.widget.value_from_datadict(self.form.data, self.html_name)
|
||||
data = property(_data)
|
||||
|
||||
def _verbose_name(self):
|
||||
return pretty_name(self.name)
|
||||
verbose_name = property(_verbose_name)
|
||||
|
||||
def label_tag(self, contents=None):
|
||||
"""
|
||||
Wraps the given contents in a <label>, if the field has an ID attribute.
|
||||
Does not HTML-escape the contents. If contents aren't given, uses the
|
||||
field's HTML-escaped verbose_name.
|
||||
field's HTML-escaped label.
|
||||
"""
|
||||
contents = contents or escape(self.verbose_name)
|
||||
contents = contents or escape(self.label)
|
||||
widget = self.field.widget
|
||||
id_ = widget.attrs.get('id') or self.auto_id
|
||||
if id_:
|
||||
@ -242,8 +272,8 @@ class BoundField(StrAndUnicode):
|
||||
"""
|
||||
auto_id = self.form.auto_id
|
||||
if auto_id and '%s' in str(auto_id):
|
||||
return str(auto_id) % self.name
|
||||
return str(auto_id) % self.html_name
|
||||
elif auto_id:
|
||||
return self.name
|
||||
return self.html_name
|
||||
return ''
|
||||
auto_id = property(_auto_id)
|
||||
|
@ -1,13 +1,75 @@
|
||||
"""
|
||||
Helper functions for creating Forms from Django models and database field objects.
|
||||
Helper functions for creating Form classes from Django models
|
||||
and database field objects.
|
||||
"""
|
||||
|
||||
__all__ = ('form_for_model', 'form_for_fields')
|
||||
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
|
||||
|
||||
def form_for_model(model):
|
||||
"Returns a Form instance for the given Django model class."
|
||||
raise NotImplementedError
|
||||
__all__ = ('form_for_model', 'form_for_instance', 'form_for_fields')
|
||||
|
||||
def create(self, save=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)
|
||||
obj = self._model(**self.clean_data)
|
||||
if save:
|
||||
obj.save()
|
||||
return obj
|
||||
|
||||
def make_apply_changes(opts, instance):
|
||||
"Returns the apply_changes() method for a form_for_instance Form."
|
||||
from django.db import models
|
||||
def apply_changes(self, save=True):
|
||||
if self.errors:
|
||||
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
||||
clean_data = self.clean_data
|
||||
for f in opts.fields + opts.many_to_many:
|
||||
if isinstance(f, models.AutoField):
|
||||
continue
|
||||
setattr(instance, f.attname, clean_data[f.name])
|
||||
if save:
|
||||
instance.save()
|
||||
return instance
|
||||
return apply_changes
|
||||
|
||||
def form_for_model(model, form=BaseForm):
|
||||
"""
|
||||
Returns a Form class for the given Django model class.
|
||||
|
||||
Provide 'form' if you want to use a custom BaseForm subclass.
|
||||
"""
|
||||
opts = model._meta
|
||||
field_list = []
|
||||
for f in opts.fields + opts.many_to_many:
|
||||
formfield = f.formfield()
|
||||
if formfield:
|
||||
field_list.append((f.name, formfield))
|
||||
fields = SortedDictFromList(field_list)
|
||||
return type(opts.object_name + 'Form', (form,), {'fields': fields, '_model': model, 'create': create})
|
||||
|
||||
def form_for_instance(instance, form=BaseForm):
|
||||
"""
|
||||
Returns a Form class for the given Django model instance.
|
||||
|
||||
Provide 'form' if you want to use a custom BaseForm subclass.
|
||||
"""
|
||||
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 = f.formfield(initial=current_value)
|
||||
if formfield:
|
||||
field_list.append((f.name, formfield))
|
||||
fields = SortedDictFromList(field_list)
|
||||
return type(opts.object_name + 'InstanceForm', (form,),
|
||||
{'fields': fields, '_model': model, 'apply_changes': make_apply_changes(opts, instance)})
|
||||
|
||||
def form_for_fields(field_list):
|
||||
"Returns a Form instance for the given list of Django database field instances."
|
||||
raise NotImplementedError
|
||||
"Returns a Form class for the given list of Django database field instances."
|
||||
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
|
||||
return type('FormForFields', (BaseForm,), {'fields': fields})
|
||||
|
@ -9,6 +9,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
from util import StrAndUnicode, smart_unicode
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.html import escape
|
||||
from itertools import chain
|
||||
|
||||
@ -22,13 +23,18 @@ except NameError:
|
||||
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
||||
|
||||
class Widget(object):
|
||||
requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
|
||||
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
self.attrs = attrs or {}
|
||||
|
||||
def render(self, name, value):
|
||||
def render(self, name, value, attrs=None):
|
||||
"""
|
||||
Returns this Widget rendered as HTML, as a Unicode string.
|
||||
|
||||
The 'value' given is not guaranteed to be valid input, so subclass
|
||||
implementations should program defensively.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||
@ -64,6 +70,7 @@ class Input(Widget):
|
||||
type='radio', which are special).
|
||||
"""
|
||||
input_type = None # Subclasses must define this.
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
if value is None: value = ''
|
||||
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
||||
@ -128,7 +135,6 @@ class Select(Widget):
|
||||
return u'\n'.join(output)
|
||||
|
||||
class SelectMultiple(Widget):
|
||||
requires_data_list = True
|
||||
def __init__(self, attrs=None, choices=()):
|
||||
# choices can be any iterable
|
||||
self.attrs = attrs or {}
|
||||
@ -146,6 +152,11 @@ class SelectMultiple(Widget):
|
||||
output.append(u'</select>')
|
||||
return u'\n'.join(output)
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
if isinstance(data, MultiValueDict):
|
||||
return data.getlist(name)
|
||||
return data.get(name, None)
|
||||
|
||||
class RadioInput(StrAndUnicode):
|
||||
"An object used by RadioFieldRenderer that represents a single <input type='radio'>."
|
||||
def __init__(self, name, value, attrs, choice, index):
|
||||
@ -178,6 +189,10 @@ class RadioFieldRenderer(StrAndUnicode):
|
||||
for i, choice in enumerate(self.choices):
|
||||
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
choice = self.choices[idx] # Let the IndexError propogate
|
||||
return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
|
||||
|
||||
def __unicode__(self):
|
||||
"Outputs a <ul> for this set of radio fields."
|
||||
return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % w for w in self])
|
||||
|
1008
django/oldforms/__init__.py
Normal file
1008
django/oldforms/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ Usage:
|
||||
>>>
|
||||
"""
|
||||
|
||||
from django.utils.dates import MONTHS, MONTHS_AP, WEEKDAYS
|
||||
from django.utils.dates import MONTHS, MONTHS_3, MONTHS_AP, WEEKDAYS
|
||||
from django.utils.tzinfo import LocalTimezone
|
||||
from calendar import isleap, monthrange
|
||||
import re, time
|
||||
@ -147,7 +147,7 @@ class DateFormat(TimeFormat):
|
||||
|
||||
def M(self):
|
||||
"Month, textual, 3 letters; e.g. 'Jan'"
|
||||
return MONTHS[self.data.month][0:3]
|
||||
return MONTHS_3[self.data.month].title()
|
||||
|
||||
def n(self):
|
||||
"Month without leading zeros; i.e. '1' to '12'"
|
||||
|
@ -8,17 +8,28 @@ capfirst = lambda x: x and x[0].upper() + x[1:]
|
||||
def wrap(text, width):
|
||||
"""
|
||||
A word-wrap function that preserves existing line breaks and most spaces in
|
||||
the text. Expects that existing line breaks are posix newlines (\n).
|
||||
See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
|
||||
the text. Expects that existing line breaks are posix newlines.
|
||||
"""
|
||||
return reduce(lambda line, word, width=width: '%s%s%s' %
|
||||
(line,
|
||||
' \n'[(len(line[line.rfind('\n')+1:])
|
||||
+ len(word.split('\n',1)[0]
|
||||
) >= width)],
|
||||
word),
|
||||
text.split(' ')
|
||||
)
|
||||
def _generator():
|
||||
it = iter(text.split(' '))
|
||||
word = it.next()
|
||||
yield word
|
||||
pos = len(word) - word.rfind('\n') - 1
|
||||
for word in it:
|
||||
if "\n" in word:
|
||||
lines = word.splitlines()
|
||||
else:
|
||||
lines = (word,)
|
||||
pos += len(lines[0]) + 1
|
||||
if pos > width:
|
||||
yield '\n'
|
||||
pos = len(lines[-1])
|
||||
else:
|
||||
yield ' '
|
||||
if len(lines) > 1:
|
||||
pos = len(lines[-1])
|
||||
yield word
|
||||
return "".join(_generator())
|
||||
|
||||
def truncate_words(s, num):
|
||||
"Truncates a string after a certain number of words."
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.core.xheaders import populate_xheaders
|
||||
from django.template import loader
|
||||
from django import forms
|
||||
from django import oldforms
|
||||
from django.db.models import FileField
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
from django.template import RequestContext
|
||||
@ -56,7 +56,7 @@ def create_object(request, model, template_name=None,
|
||||
new_data = manipulator.flatten_data()
|
||||
|
||||
# Create the FormWrapper, template, context, response
|
||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
if not template_name:
|
||||
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
@ -128,7 +128,7 @@ def update_object(request, model, object_id=None, slug=None,
|
||||
# This makes sure the form acurate represents the fields of the place.
|
||||
new_data = manipulator.flatten_data()
|
||||
|
||||
form = forms.FormWrapper(manipulator, new_data, errors)
|
||||
form = oldforms.FormWrapper(manipulator, new_data, errors)
|
||||
if not template_name:
|
||||
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
|
@ -22,6 +22,9 @@ of the community, so there are many ways you can help Django's development:
|
||||
likely to be skeptical of large-scale suggestions without some code to
|
||||
back it up.
|
||||
|
||||
* Triage patches that have been submitted by other users. Please read
|
||||
`Ticket triage`_ below, for details on the triage process.
|
||||
|
||||
That's all you need to know if you'd like to join the Django development
|
||||
community. The rest of this document describes the details of how our community
|
||||
works and how it handles bugs, mailing lists, and all the other minutiae of
|
||||
@ -44,8 +47,10 @@ particular:
|
||||
|
||||
* **Do** write complete, reproducible, specific bug reports. Include as
|
||||
much information as you possibly can, complete with code snippets, test
|
||||
cases, etc. A minimal example that illustrates the bug in a nice small
|
||||
test case is the best possible bug report.
|
||||
cases, etc. This means including a clear, concise description of the
|
||||
problem, and a clear set of instructions for replicating the problem.
|
||||
A minimal example that illustrates the bug in a nice small test case
|
||||
is the best possible bug report.
|
||||
|
||||
* **Don't** use the ticket system to ask support questions. Use the
|
||||
`django-users`_ list, or the `#django`_ IRC channel for that.
|
||||
@ -121,6 +126,50 @@ Patch style
|
||||
it obvious that the ticket includes a patch, and it will add the ticket
|
||||
to the `list of tickets with patches`_.
|
||||
|
||||
* The code required to fix a problem or add a feature is an essential part
|
||||
of a patch, but it is not the only part. A good patch should also include
|
||||
a regression test to validate the behavior that has been fixed (and prevent
|
||||
the problem from arising again).
|
||||
|
||||
* If the code associated with a patch adds a new feature, or modifies behavior
|
||||
of an existing feature, the patch should also contain documentation.
|
||||
|
||||
Non-trivial patches
|
||||
-------------------
|
||||
|
||||
A "non-trivial" patch is one that is more than a simple bug fix. It's a patch
|
||||
that introduces Django functionality and makes some sort of design decision.
|
||||
|
||||
If you provide a non-trivial patch, include evidence that alternatives have
|
||||
been discussed on `django-developers`_. If you're not sure whether your patch
|
||||
should be considered non-trivial, just ask.
|
||||
|
||||
Ticket triage
|
||||
=============
|
||||
|
||||
Unfortunately, not all bug reports in the `ticket tracker`_ provide all
|
||||
the `required details`_. A number of tickets have patches, but those patches
|
||||
don't meet all the requirements of a `good patch`_.
|
||||
|
||||
One way to help out is to *triage* bugs that have been reported by other users.
|
||||
Pick an open ticket that is missing some details, and try to replicate the
|
||||
problem. Fill in the missing pieces of the report. If the ticket doesn't have
|
||||
a patch, create one.
|
||||
|
||||
Once you've completed all the missing details on the ticket and you have a
|
||||
patch with all the required features, e-mail `django-developers`_. Indicate
|
||||
that you have triaged a ticket, and recommend a course of action for dealing
|
||||
with that ticket.
|
||||
|
||||
At first, this may require you to be persistent. If you find that your triaged
|
||||
ticket still isn't getting attention, occasional polite requests for eyeballs
|
||||
to look at your ticket may be necessary. However, as you earn a reputation for
|
||||
quality triage work, you should find that it is easier to get the developers'
|
||||
attention.
|
||||
|
||||
.. _required details: `Reporting bugs`_
|
||||
.. _good patch: `Patch style`_
|
||||
|
||||
Submitting and maintaining translations
|
||||
=======================================
|
||||
|
||||
@ -338,21 +387,63 @@ trunk more than once.
|
||||
Using branches
|
||||
--------------
|
||||
|
||||
To test a given branch, you can simply check out the entire branch, like so::
|
||||
To use a branch, you'll need to do two things:
|
||||
|
||||
* Get the branch's code through Subversion.
|
||||
|
||||
* Point your Python ``site-packages`` directory at the branch's version of
|
||||
the ``django`` package rather than the version you already have
|
||||
installed.
|
||||
|
||||
Getting the code from Subversion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To get the latest version of a branch's code, check it out using Subversion::
|
||||
|
||||
svn co http://code.djangoproject.com/svn/django/branches/<branch>/
|
||||
|
||||
Or, if you've got a working directory you'd like to switch to use a branch,
|
||||
you can use::
|
||||
...where ``<branch>`` is the branch's name. See the `list of branch names`_.
|
||||
|
||||
Alternatively, you can automatically convert an existing directory of the
|
||||
Django source code as long as you've checked it out via Subversion. To do the
|
||||
conversion, execute this command from within your ``django`` directory::
|
||||
|
||||
svn switch http://code.djangoproject.com/svn/django/branches/<branch>/
|
||||
|
||||
...in the root of your Django sandbox (the directory that contains ``django``,
|
||||
``docs``, and ``tests``).
|
||||
|
||||
The advantage of using ``svn switch`` instead of ``svn co`` is that the
|
||||
``switch`` command retains any changes you might have made to your local copy
|
||||
of the code. It attempts to merge those changes into the "switched" code.
|
||||
of the code. It attempts to merge those changes into the "switched" code. The
|
||||
disadvantage is that it may cause conflicts with your local changes if the
|
||||
"switched" code has altered the same lines of code.
|
||||
|
||||
(Note that if you use ``svn switch``, you don't need to point Python at the new
|
||||
version, as explained in the next section.)
|
||||
|
||||
.. _list of branch names: http://code.djangoproject.com/browser/django/branches
|
||||
|
||||
Pointing Python at the new Django version
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Once you've retrieved the branch's code, you'll need to change your Python
|
||||
``site-packages`` directory so that it points to the branch version of the
|
||||
``django`` directory. (The ``site-packages`` directory is somewhere such as
|
||||
``/usr/lib/python2.4/site-packages`` or
|
||||
``/usr/local/lib/python2.4/site-packages`` or ``C:\Python\site-packages``.)
|
||||
|
||||
The simplest way to do this is by renaming the old ``django`` directory to
|
||||
``django.OLD`` and moving the trunk version of the code into the directory
|
||||
and calling it ``django``.
|
||||
|
||||
Alternatively, you can use a symlink called ``django`` that points to the
|
||||
location of the branch's ``django`` package. If you want to switch back, just
|
||||
change the symlink to point to the old code.
|
||||
|
||||
If you're using Django 0.95 or earlier and installed it using
|
||||
``python setup.py install``, you'll have a directory called something like
|
||||
``Django-0.95-py2.4.egg`` instead of ``django``. In this case, edit the file
|
||||
``setuptools.pth`` and remove the line that references the Django ``.egg``
|
||||
file. Then copy the branch's version of the ``django`` directory into
|
||||
``site-packages``.
|
||||
|
||||
Official releases
|
||||
=================
|
||||
|
@ -1,9 +1,9 @@
|
||||
=====================================
|
||||
Cross Site Request Forgery Protection
|
||||
Cross Site Request Forgery protection
|
||||
=====================================
|
||||
|
||||
The CsrfMiddleware class provides easy-to-use protection against
|
||||
`Cross Site Request Forgeries`_. This type of attack occurs when a malicious
|
||||
The CsrfMiddleware class provides easy-to-use protection against
|
||||
`Cross Site Request Forgeries`_. This type of attack occurs when a malicious
|
||||
web site creates a link or form button that is intended to perform some action
|
||||
on your web site, using the credentials of a logged-in user who is tricked
|
||||
into clicking on the link in their browser.
|
||||
@ -12,12 +12,12 @@ The first defense against CSRF attacks is to ensure that GET requests
|
||||
are side-effect free. POST requests can then be protected by adding this
|
||||
middleware into your list of installed middleware.
|
||||
|
||||
|
||||
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
|
||||
|
||||
How to use it
|
||||
=============
|
||||
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
|
||||
|
||||
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
|
||||
your list of middleware classes, ``MIDDLEWARE_CLASSES``. It needs to process
|
||||
the response after the SessionMiddleware, so must come before it in the
|
||||
list. It also must process the response before things like compression
|
||||
@ -25,16 +25,17 @@ happen to the response, so it must come after GZipMiddleware in the list.
|
||||
|
||||
How it works
|
||||
============
|
||||
|
||||
CsrfMiddleware does two things:
|
||||
|
||||
1. It modifies outgoing requests by adding a hidden form field to all
|
||||
'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is
|
||||
a hash of the session ID plus a secret. If there is no session ID set,
|
||||
this modification of the response isn't done, so there is very little
|
||||
1. It modifies outgoing requests by adding a hidden form field to all
|
||||
'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is
|
||||
a hash of the session ID plus a secret. If there is no session ID set,
|
||||
this modification of the response isn't done, so there is very little
|
||||
performance penalty for those requests that don't have a session.
|
||||
|
||||
2. On all incoming POST requests that have the session cookie set, it
|
||||
checks that the 'csrfmiddlewaretoken' is present and correct. If it
|
||||
2. On all incoming POST requests that have the session cookie set, it
|
||||
checks that the 'csrfmiddlewaretoken' is present and correct. If it
|
||||
isn't, the user will get a 403 error.
|
||||
|
||||
This ensures that only forms that have originated from your web site
|
||||
@ -43,26 +44,26 @@ can be used to POST data back.
|
||||
It deliberately only targets HTTP POST requests (and the corresponding
|
||||
POST forms). GET requests ought never to have side effects (if you are
|
||||
using HTTP GET and POST correctly), and so a CSRF attack with a GET
|
||||
request will always be harmless.
|
||||
request will always be harmless.
|
||||
|
||||
POST requests that are not accompanied by a session cookie are not protected,
|
||||
but they do not need to be protected, since the 'attacking' web site
|
||||
could make these kind of requests anyway.
|
||||
|
||||
The Content-Type is checked before modifying the response, and only
|
||||
The Content-Type is checked before modifying the response, and only
|
||||
pages that are served as 'text/html' or 'application/xml+xhtml'
|
||||
are modified.
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
CsrfMiddleware requires Django's session framework to work. If you have
|
||||
a custom authentication system that manually sets cookies and the like,
|
||||
it won't help you.
|
||||
|
||||
If your app creates HTML pages and forms in some unusual way, (e.g.
|
||||
it sends fragments of HTML in javascript document.write statements)
|
||||
you might bypass the filter that adds the hidden field to the form,
|
||||
If your app creates HTML pages and forms in some unusual way, (e.g.
|
||||
it sends fragments of HTML in javascript document.write statements)
|
||||
you might bypass the filter that adds the hidden field to the form,
|
||||
in which case form submission will always fail. It may still be possible
|
||||
to use the middleware, provided you can find some way to get the
|
||||
CSRF token and ensure that is included when your form is submitted.
|
||||
|
||||
CSRF token and ensure that is included when your form is submitted.
|
@ -143,9 +143,9 @@ or ``UPDATE`` SQL statements. Specifically, when you call ``save()``, Django
|
||||
follows this algorithm:
|
||||
|
||||
* If the object's primary key attribute is set to a value that evaluates to
|
||||
``False`` (such as ``None`` or the empty string), Django executes a
|
||||
``SELECT`` query to determine whether a record with the given primary key
|
||||
already exists.
|
||||
``True`` (i.e., a value other than ``None`` or the empty string), Django
|
||||
executes a ``SELECT`` query to determine whether a record with the given
|
||||
primary key already exists.
|
||||
* If the record with the given primary key does already exist, Django
|
||||
executes an ``UPDATE`` query.
|
||||
* If the object's primary key attribute is *not* set, or if it's set but a
|
||||
|
@ -2,15 +2,27 @@
|
||||
Forms, fields, and manipulators
|
||||
===============================
|
||||
|
||||
Forwards-compatibility note
|
||||
===========================
|
||||
|
||||
The legacy forms/manipulators system described in this document is going to be
|
||||
replaced in the next Django release. If you're starting from scratch, we
|
||||
strongly encourage you not to waste your time learning this. Instead, learn and
|
||||
use the django.newforms system, which we have begun to document in the
|
||||
`newforms documentation`_.
|
||||
|
||||
If you have legacy form/manipulator code, read the "Migration plan" section in
|
||||
that document to understand how we're making the switch.
|
||||
|
||||
.. _newforms documentation: http://www.djangoproject.com/documentation/newforms/
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Once you've got a chance to play with Django's admin interface, you'll probably
|
||||
wonder if the fantastic form validation framework it uses is available to user
|
||||
code. It is, and this document explains how the framework works.
|
||||
|
||||
.. admonition:: A note to the lazy
|
||||
|
||||
If all you want to do is present forms for a user to create and/or
|
||||
update a given object, you may be able to use `generic views`_.
|
||||
|
||||
We'll take a top-down approach to examining Django's form validation framework,
|
||||
because much of the time you won't need to use the lower-level APIs. Throughout
|
||||
this document, we'll be working with the following model, a "place" object::
|
||||
@ -41,17 +53,17 @@ this document, we'll be working with the following model, a "place" object::
|
||||
Defining the above class is enough to create an admin interface to a ``Place``,
|
||||
but what if you want to allow public users to submit places?
|
||||
|
||||
Manipulators
|
||||
============
|
||||
Automatic Manipulators
|
||||
======================
|
||||
|
||||
The highest-level interface for object creation and modification is the
|
||||
**Manipulator** framework. A manipulator is a utility class tied to a given
|
||||
model that "knows" how to create or modify instances of that model and how to
|
||||
validate data for the object. Manipulators come in two flavors:
|
||||
``AddManipulators`` and ``ChangeManipulators``. Functionally they are quite
|
||||
similar, but the former knows how to create new instances of the model, while
|
||||
the latter modifies existing instances. Both types of classes are automatically
|
||||
created when you define a new class::
|
||||
**automatic Manipulator** framework. An automatic manipulator is a utility
|
||||
class tied to a given model that "knows" how to create or modify instances of
|
||||
that model and how to validate data for the object. Automatic Manipulators come
|
||||
in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally
|
||||
they are quite similar, but the former knows how to create new instances of the
|
||||
model, while the latter modifies existing instances. Both types of classes are
|
||||
automatically created when you define a new class::
|
||||
|
||||
>>> from mysite.myapp.models import Place
|
||||
>>> Place.AddManipulator
|
||||
|
@ -902,7 +902,7 @@ If ``template_name`` isn't specified, this view will use the template
|
||||
|
||||
In addition to ``extra_context``, the template's context will be:
|
||||
|
||||
* ``form``: A ``django.forms.FormWrapper`` instance representing the form
|
||||
* ``form``: A ``django.oldforms.FormWrapper`` instance representing the form
|
||||
for editing the object. This lets you refer to form fields easily in the
|
||||
template system.
|
||||
|
||||
@ -984,7 +984,7 @@ If ``template_name`` isn't specified, this view will use the template
|
||||
|
||||
In addition to ``extra_context``, the template's context will be:
|
||||
|
||||
* ``form``: A ``django.forms.FormWrapper`` instance representing the form
|
||||
* ``form``: A ``django.oldforms.FormWrapper`` instance representing the form
|
||||
for editing the object. This lets you refer to form fields easily in the
|
||||
template system.
|
||||
|
||||
|
@ -22,7 +22,6 @@ what the name of the database is. Do that by editing these settings in your
|
||||
* `DATABASE_ENGINE`_
|
||||
* `DATABASE_USER`_
|
||||
* `DATABASE_PASSWORD`_
|
||||
* `DATABASE_NAME`_
|
||||
* `DATABASE_HOST`_
|
||||
* `DATABASE_PORT`_
|
||||
|
||||
@ -31,7 +30,6 @@ what the name of the database is. Do that by editing these settings in your
|
||||
.. _DATABASE_ENGINE: http://www.djangoproject.com/documentation/settings/#database-engine
|
||||
.. _DATABASE_USER: http://www.djangoproject.com/documentation/settings/#database-user
|
||||
.. _DATABASE_PASSWORD: http://www.djangoproject.com/documentation/settings/#database-password
|
||||
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name
|
||||
.. _DATABASE_HOST: http://www.djangoproject.com/documentation/settings/#database-host
|
||||
.. _DATABASE_PORT: http://www.djangoproject.com/documentation/settings/#database-port
|
||||
|
||||
|
@ -13,18 +13,23 @@ Migration plan
|
||||
-- i.e., it's not available in the Django 0.95 release. For the next Django
|
||||
release, our plan is to do the following:
|
||||
|
||||
* Move the current ``django.forms`` to ``django.oldforms``. This will allow
|
||||
for an eased migration of form code. You'll just have to change your
|
||||
import statements::
|
||||
* As of revision [4208], we've copied the current ``django.forms`` to
|
||||
``django.oldforms``. This allows you to upgrade your code *now* rather
|
||||
than waiting for the backwards-incompatible change and rushing to fix
|
||||
your code after the fact. Just change your import statements like this::
|
||||
|
||||
from django import forms # old
|
||||
from django import oldforms as forms # new
|
||||
|
||||
* Move the current ``django.newforms`` to ``django.forms``.
|
||||
* At an undecided future date, we will move the current ``django.newforms``
|
||||
to ``django.forms``. This will be a backwards-incompatible change, and
|
||||
anybody who is still using the old version of ``django.forms`` at that
|
||||
time will need to change their import statements, as described in the
|
||||
previous bullet.
|
||||
|
||||
* We will remove ``django.oldforms`` in the release *after* the next Django
|
||||
release -- the release that comes after the release in which we're
|
||||
creating ``django.oldforms``.
|
||||
creating the new ``django.forms``.
|
||||
|
||||
With this in mind, we recommend you use the following import statement when
|
||||
using ``django.newforms``::
|
||||
@ -46,9 +51,14 @@ too messy. The choice is yours.
|
||||
Overview
|
||||
========
|
||||
|
||||
As the ``django.forms`` system before it, ``django.newforms`` is intended to
|
||||
handle HTML form display, validation and redisplay. It's what you use if you
|
||||
want to perform server-side validation for an HTML form.
|
||||
As the ``django.forms`` ("manipulators") system before it, ``django.newforms``
|
||||
is intended to handle HTML form display, validation and redisplay. It's what
|
||||
you use if you want to perform server-side validation for an HTML form.
|
||||
|
||||
For example, if your Web site has a contact form that visitors can use to
|
||||
send you e-mail, you'd use this library to implement the display of the HTML
|
||||
form fields, along with the form validation. Any time you need to use an HTML
|
||||
``<form>``, you can use this library.
|
||||
|
||||
The library deals with these concepts:
|
||||
|
||||
@ -62,6 +72,223 @@ The library deals with these concepts:
|
||||
* **Form** -- A collection of fields that knows how to validate itself and
|
||||
display itself as HTML.
|
||||
|
||||
Form objects
|
||||
============
|
||||
|
||||
The primary way of using the ``newforms`` library is to create a form object.
|
||||
Do this by subclassing ``django.newforms.Form`` and specifying the form's
|
||||
fields, in a declarative style that you'll be familiar with if you've used
|
||||
Django database models. In this section, we'll iteratively develop a form
|
||||
object that you might to implement "contact me" functionality on your personal
|
||||
Web site.
|
||||
|
||||
Start with this basic ``Form`` subclass, which we'll call ``ContactForm``::
|
||||
|
||||
from django import newforms as forms
|
||||
|
||||
class ContactForm(forms.Form):
|
||||
subject = forms.CharField(max_length=100)
|
||||
message = forms.CharField()
|
||||
sender = forms.EmailField()
|
||||
cc_myself = forms.BooleanField()
|
||||
|
||||
A form is composed of ``Field`` objects. In this case, our form has four
|
||||
fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain
|
||||
the different types of fields -- e.g., ``CharField`` and ``EmailField`` --
|
||||
shortly.
|
||||
|
||||
Outputting forms as HTML
|
||||
------------------------
|
||||
|
||||
The first thing we can do with this is output it as HTML. To do so, instantiate
|
||||
it and ``print`` it::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> print f
|
||||
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
|
||||
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
|
||||
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
|
||||
|
||||
This default output is a two-column HTML table, with a ``<tr>`` for each field.
|
||||
Notice the following:
|
||||
|
||||
* For flexibility, the output does *not* include the ``<table>`` and
|
||||
``</table>`` tags, nor does it include the ``<form>`` and ``</form>``
|
||||
tags or an ``<input type="submit">`` tag. It's your job to do that.
|
||||
|
||||
* Each field type has a default HTML representation. ``CharField`` and
|
||||
``EmailField`` are represented by an ``<input type="text">``.
|
||||
``BooleanField`` is represented by an ``<input type="checkbox">``. Note
|
||||
these are merely sensible defaults; you can specify which HTML to use for
|
||||
a given field by using ``widgets``, which we'll explain shortly.
|
||||
|
||||
* The HTML ``name`` for each tag is taken directly from its attribute name
|
||||
in the ``ContactForm`` class.
|
||||
|
||||
* The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and
|
||||
``'CC myself:'`` is generated from the field name by converting all
|
||||
underscores to spaces and upper-casing the first letter. Again, note
|
||||
these are merely sensible defaults; you can also specify labels manually.
|
||||
|
||||
* Each text label is surrounded in an HTML ``<label>`` tag, which points
|
||||
to the appropriate form field via its ``id``. Its ``id``, in turn, is
|
||||
generated by prepending ``'id_'`` to the field name. The ``id``
|
||||
attributes and ``<label>`` tags are included in the output by default, to
|
||||
follow best practices, but you can change that behavior.
|
||||
|
||||
Although ``<table>`` output is the default output style when you ``print`` a
|
||||
form, other output styles are available. Each style is available as a method on
|
||||
a form object, and each rendering method returns a Unicode object.
|
||||
|
||||
``as_p()``
|
||||
~~~~~~~~~~
|
||||
|
||||
``Form.as_p()`` renders the form as a series of ``<p>`` tags, with each ``<p>``
|
||||
containing one field::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.as_p()
|
||||
u'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>'
|
||||
>>> print f.as_p()
|
||||
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>
|
||||
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>
|
||||
<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>
|
||||
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
|
||||
|
||||
``as_ul()``
|
||||
~~~~~~~~~~~
|
||||
|
||||
``Form.as_ul()`` renders the form as a series of ``<li>`` tags, with each
|
||||
``<li>`` containing one field. It does *not* include the ``<ul>`` or ``</ul>``,
|
||||
so that you can specify any HTML attributes on the ``<ul>`` for flexibility::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.as_ul()
|
||||
u'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>'
|
||||
>>> print f.as_ul()
|
||||
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>
|
||||
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>
|
||||
<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>
|
||||
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>
|
||||
|
||||
``as_table()``
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Finally, ``Form.as_table()`` outputs the form as an HTML ``<table>``. This is
|
||||
exactly the same as ``print``. In fact, when you ``print`` a form object, it
|
||||
calls its ``as_table()`` method behind the scenes::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.as_table()
|
||||
u'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>'
|
||||
>>> print f.as_table()
|
||||
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
|
||||
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
|
||||
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
|
||||
|
||||
Configuring HTML ``<label>`` tags
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An HTML ``<label>`` tag designates which label text is associated with which
|
||||
form element. This small enhancement makes forms more usable and more accessible
|
||||
to assistive devices. It's always a good idea to use ``<label>`` tags.
|
||||
|
||||
By default, the form rendering methods include HTML ``id`` attributes on the
|
||||
form elements and corresponding ``<label>`` tags around the labels. The ``id``
|
||||
attribute values are generated by prepending ``id_`` to the form field names.
|
||||
This behavior is configurable, though, if you want to change the ``id``
|
||||
convention or remove HTML ``id`` attributes and ``<label>`` tags entirely.
|
||||
|
||||
Use the ``auto_id`` argument to the ``Form`` constructor to control the label
|
||||
and ``id`` behavior. This argument must be ``True``, ``False`` or a string.
|
||||
|
||||
If ``auto_id`` is ``False``, then the form output will not include ``<label>``
|
||||
tags nor ``id`` attributes::
|
||||
|
||||
>>> f = ContactForm(auto_id=False)
|
||||
>>> print f.as_table()
|
||||
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
|
||||
<tr><th>Sender:</th><td><input type="text" name="sender" /></td></tr>
|
||||
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li>Subject: <input type="text" name="subject" maxlength="100" /></li>
|
||||
<li>Message: <input type="text" name="message" /></li>
|
||||
<li>Sender: <input type="text" name="sender" /></li>
|
||||
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
|
||||
>>> print f.as_p()
|
||||
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
|
||||
<p>Message: <input type="text" name="message" /></p>
|
||||
<p>Sender: <input type="text" name="sender" /></p>
|
||||
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
|
||||
|
||||
If ``auto_id`` is set to ``True``, then the form output *will* include
|
||||
``<label>`` tags and will simply use the field name as its ``id`` for each form
|
||||
field::
|
||||
|
||||
>>> f = ContactForm(auto_id=True)
|
||||
>>> print f.as_table()
|
||||
<tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr>
|
||||
<tr><th><label for="sender">Sender:</label></th><td><input type="text" name="sender" id="sender" /></td></tr>
|
||||
<tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></li>
|
||||
<li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li>
|
||||
<li><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></li>
|
||||
<li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li>
|
||||
>>> print f.as_p()
|
||||
<p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></p>
|
||||
<p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p>
|
||||
<p><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></p>
|
||||
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p>
|
||||
|
||||
If ``auto_id`` is set to a string containing the format character ``'%s'``,
|
||||
then the form output will include ``<label>`` tags, and will generate ``id``
|
||||
attributes based on the format string. For example, for a format string
|
||||
``'field_%s'``, a field named ``subject`` will get the ``id``
|
||||
``'field_subject'``. Continuing our example::
|
||||
|
||||
>>> f = ContactForm(auto_id='id_for_%s')
|
||||
>>> print f.as_table()
|
||||
<tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" /></td></tr>
|
||||
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr>
|
||||
<tr><th><label for="id_for_sender">Sender:</label></th><td><input type="text" name="sender" id="id_for_sender" /></td></tr>
|
||||
<tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
|
||||
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li>
|
||||
<li><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></li>
|
||||
<li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
|
||||
>>> print f.as_p()
|
||||
<p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></p>
|
||||
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p>
|
||||
<p><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></p>
|
||||
<p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p>
|
||||
|
||||
If ``auto_id`` is set to any other true value -- such as a string that doesn't
|
||||
include ``%s`` -- then the library will act as if ``auto_id`` is ``True``.
|
||||
|
||||
By default, ``auto_id`` is set to the string ``'id_%s'``.
|
||||
|
||||
Notes on field ordering
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the ``as_p()``, ``as_ul()`` and ``as_table()`` shortcuts, the fields are
|
||||
displayed in the order in which you define them in your form class. For
|
||||
example, in the ``ContactForm`` example, the fields are defined in the order
|
||||
``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML
|
||||
output, just change the order in which those fields are listed in the class.
|
||||
|
||||
Using forms to validate data
|
||||
----------------------------
|
||||
|
||||
In addition to HTML form display, a ``Form`` class is responsible for
|
||||
validating data.
|
||||
|
||||
|
||||
More coming soon
|
||||
================
|
||||
|
||||
@ -69,3 +296,9 @@ That's all the documentation for now. For more, see the file
|
||||
http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py
|
||||
-- the unit tests for ``django.newforms``. This can give you a good idea of
|
||||
what's possible.
|
||||
|
||||
Using forms with templates
|
||||
==========================
|
||||
|
||||
Using forms in views
|
||||
====================
|
||||
|
@ -63,9 +63,9 @@ Via the Python API
|
||||
------------------
|
||||
|
||||
Redirects are represented by a standard `Django model`_, which lives in
|
||||
`django/contrib/redirects/models/redirects.py`_. You can access redirect
|
||||
`django/contrib/redirects/models.py`_. You can access redirect
|
||||
objects via the `Django database API`_.
|
||||
|
||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _django/contrib/redirects/models/redirects.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models/redirects.py
|
||||
.. _django/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/
|
||||
|
@ -871,7 +871,7 @@ TIME_FORMAT and MONTH_DAY_FORMAT.
|
||||
.. _cache docs: http://www.djangoproject.com/documentation/cache/
|
||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _session docs: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _See available choices: http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||
.. _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/
|
||||
|
||||
Creating your own settings
|
||||
|
5
setup.py
5
setup.py
@ -25,9 +25,12 @@ for dirpath, dirnames, filenames in os.walk(django_dir):
|
||||
else:
|
||||
data_files.append((dirpath, [os.path.join(dirpath, f) for f in filenames]))
|
||||
|
||||
# Dynamically calculate the version based on django.VERSION.
|
||||
version = "%d.%d-%s" % (__import__('django').VERSION)
|
||||
|
||||
setup(
|
||||
name = "Django",
|
||||
version = "0.95",
|
||||
version = version,
|
||||
url = 'http://www.djangoproject.com/',
|
||||
author = 'Lawrence Journal-World',
|
||||
author_email = 'holovaty@gmail.com',
|
||||
|
@ -10,6 +10,9 @@ class Article(models.Model):
|
||||
headline = models.CharField(maxlength=100, default='Default headline')
|
||||
pub_date = models.DateTimeField()
|
||||
|
||||
class Meta:
|
||||
ordering = ('pub_date','headline')
|
||||
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
@ -245,7 +248,7 @@ datetime.datetime(2005, 7, 28, 0, 0)
|
||||
|
||||
# Slices (without step) are lazy:
|
||||
>>> Article.objects.all()[0:5].filter()
|
||||
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Fourth article>, <Article: Article 6>]
|
||||
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Article 6>, <Article: Default headline>]
|
||||
|
||||
# Slicing again works:
|
||||
>>> Article.objects.all()[0:5][0:2]
|
||||
@ -253,17 +256,17 @@ datetime.datetime(2005, 7, 28, 0, 0)
|
||||
>>> Article.objects.all()[0:5][:2]
|
||||
[<Article: Area woman programs in Python>, <Article: Second article>]
|
||||
>>> Article.objects.all()[0:5][4:]
|
||||
[<Article: Article 6>]
|
||||
[<Article: Default headline>]
|
||||
>>> Article.objects.all()[0:5][5:]
|
||||
[]
|
||||
|
||||
# Some more tests!
|
||||
>>> Article.objects.all()[2:][0:2]
|
||||
[<Article: Third article>, <Article: Fourth article>]
|
||||
[<Article: Third article>, <Article: Article 6>]
|
||||
>>> Article.objects.all()[2:][:2]
|
||||
[<Article: Third article>, <Article: Fourth article>]
|
||||
[<Article: Third article>, <Article: Article 6>]
|
||||
>>> Article.objects.all()[2:][2:3]
|
||||
[<Article: Article 6>]
|
||||
[<Article: Default headline>]
|
||||
|
||||
# Note that you can't use 'offset' without 'limit' (on some dbs), so this doesn't work:
|
||||
>>> Article.objects.all()[2:]
|
||||
@ -312,7 +315,7 @@ AttributeError: Manager isn't accessible via Article instances
|
||||
|
||||
# Bulk delete test: How many objects before and after the delete?
|
||||
>>> Article.objects.all()
|
||||
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Fourth article>, <Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
|
||||
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Article 6>, <Article: Default headline>, <Article: Fourth article>, <Article: Article 7>, <Article: Updated article 8>]
|
||||
>>> Article.objects.filter(id__lte=4).delete()
|
||||
>>> Article.objects.all()
|
||||
[<Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
|
||||
|
@ -231,4 +231,16 @@ __test__ = {'API_TESTS':"""
|
||||
>>> p1.article_set.all()
|
||||
[<Article: NASA uses Python>]
|
||||
|
||||
# An alternate to calling clear() is to assign the empty set
|
||||
>>> p1.article_set = []
|
||||
>>> p1.article_set.all()
|
||||
[]
|
||||
|
||||
>>> a2.publications = [p1, new_publication]
|
||||
>>> a2.publications.all()
|
||||
[<Publication: Highlights for Children>, <Publication: The Python Journal>]
|
||||
>>> a2.publications = []
|
||||
>>> a2.publications.all()
|
||||
[]
|
||||
|
||||
"""}
|
||||
|
0
tests/modeltests/model_forms/__init__.py
Normal file
0
tests/modeltests/model_forms/__init__.py
Normal file
184
tests/modeltests/model_forms/models.py
Normal file
184
tests/modeltests/model_forms/models.py
Normal file
@ -0,0 +1,184 @@
|
||||
"""
|
||||
34. Generating HTML forms from models
|
||||
|
||||
Django provides shortcuts for creating Form objects from a model class.
|
||||
|
||||
The function django.newforms.form_for_model() takes a model class and returns
|
||||
a Form that is tied to the model. This Form works just like any other Form,
|
||||
with one additional method: create(). The create() method creates an instance
|
||||
of the model and returns that newly created instance. It saves the instance to
|
||||
the database if create(save=True), which is default. If you pass
|
||||
create(save=False), then you'll get the object without saving it.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
|
||||
class Category(models.Model):
|
||||
name = models.CharField(maxlength=20)
|
||||
url = models.CharField('The URL', maxlength=40)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Writer(models.Model):
|
||||
name = models.CharField(maxlength=50)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Article(models.Model):
|
||||
headline = models.CharField(maxlength=50)
|
||||
pub_date = models.DateTimeField()
|
||||
writer = models.ForeignKey(Writer)
|
||||
categories = models.ManyToManyField(Category, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
__test__ = {'API_TESTS': """
|
||||
>>> from django.newforms import form_for_model, form_for_instance, BaseForm
|
||||
>>> import datetime
|
||||
|
||||
>>> Category.objects.all()
|
||||
[]
|
||||
|
||||
>>> CategoryForm = form_for_model(Category)
|
||||
>>> f = CategoryForm()
|
||||
>>> print f
|
||||
<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
|
||||
<tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="20" /></li>
|
||||
<li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" /></li>
|
||||
>>> print f['name']
|
||||
<input id="id_name" type="text" name="name" maxlength="20" />
|
||||
|
||||
>>> f = CategoryForm(auto_id=False)
|
||||
>>> print f.as_ul()
|
||||
<li>Name: <input type="text" name="name" maxlength="20" /></li>
|
||||
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
|
||||
|
||||
>>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
|
||||
>>> f.errors
|
||||
{}
|
||||
>>> f.clean_data
|
||||
{'url': u'entertainment', 'name': u'Entertainment'}
|
||||
>>> obj = f.create()
|
||||
>>> obj
|
||||
<Category: Entertainment>
|
||||
>>> Category.objects.all()
|
||||
[<Category: Entertainment>]
|
||||
|
||||
>>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
|
||||
>>> f.errors
|
||||
{}
|
||||
>>> f.clean_data
|
||||
{'url': u'test', 'name': u"It's a test"}
|
||||
>>> obj = f.create()
|
||||
>>> obj
|
||||
<Category: It's a test>
|
||||
>>> Category.objects.all()
|
||||
[<Category: Entertainment>, <Category: It's a test>]
|
||||
|
||||
>>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
|
||||
>>> f.errors
|
||||
{}
|
||||
>>> f.clean_data
|
||||
{'url': u'third', 'name': u'Third test'}
|
||||
>>> obj = f.create(save=False)
|
||||
>>> obj
|
||||
<Category: Third test>
|
||||
>>> Category.objects.all()
|
||||
[<Category: Entertainment>, <Category: It's a test>]
|
||||
>>> obj.save()
|
||||
>>> Category.objects.all()
|
||||
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
|
||||
|
||||
>>> f = CategoryForm({'name': '', 'url': 'foo'})
|
||||
>>> f.errors
|
||||
{'name': [u'This field is required.']}
|
||||
>>> f.clean_data
|
||||
>>> f.create()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: The Category could not be created because the data didn't validate.
|
||||
|
||||
>>> f = CategoryForm({'name': '', 'url': 'foo'})
|
||||
>>> f.create()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: The Category could not be created because the data didn't validate.
|
||||
|
||||
Create a couple of Writers.
|
||||
>>> w = Writer(name='Mike Royko')
|
||||
>>> w.save()
|
||||
>>> w = Writer(name='Bob Woodward')
|
||||
>>> w.save()
|
||||
|
||||
ManyToManyFields are represented by a MultipleChoiceField, and ForeignKeys are
|
||||
represented by a ChoiceField.
|
||||
>>> ArticleForm = form_for_model(Article)
|
||||
>>> f = ArticleForm(auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
||||
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
|
||||
<tr><th>Writer:</th><td><select name="writer">
|
||||
<option value="" selected="selected">---------</option>
|
||||
<option value="1">Mike Royko</option>
|
||||
<option value="2">Bob Woodward</option>
|
||||
</select></td></tr>
|
||||
<tr><th>Categories:</th><td><select multiple="multiple" name="categories">
|
||||
<option value="1">Entertainment</option>
|
||||
<option value="2">It's a test</option>
|
||||
<option value="3">Third test</option>
|
||||
</select></td></tr>
|
||||
|
||||
You can pass a custom Form class to form_for_model. Make sure it's a
|
||||
subclass of BaseForm, not Form.
|
||||
>>> class CustomForm(BaseForm):
|
||||
... def say_hello(self):
|
||||
... print 'hello'
|
||||
>>> CategoryForm = form_for_model(Category, form=CustomForm)
|
||||
>>> f = CategoryForm()
|
||||
>>> f.say_hello()
|
||||
hello
|
||||
|
||||
Use form_for_instance to create a Form from a model instance. There are two
|
||||
differences between this Form and one created via form_for_model. First, the
|
||||
object's current values are inserted as 'initial' data in each Field. Second,
|
||||
the Form gets an apply_changes() method instead of a create() method.
|
||||
>>> w = Writer.objects.get(name='Mike Royko')
|
||||
>>> RoykoForm = form_for_instance(w)
|
||||
>>> f = RoykoForm(auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /></td></tr>
|
||||
|
||||
>>> art = Article(headline='Test article', pub_date=datetime.date(1988, 1, 4), writer=w)
|
||||
>>> art.save()
|
||||
>>> art.id
|
||||
1
|
||||
>>> TestArticleForm = form_for_instance(art)
|
||||
>>> f = TestArticleForm(auto_id=False)
|
||||
>>> print f.as_ul()
|
||||
<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
|
||||
<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
|
||||
<li>Writer: <select name="writer">
|
||||
<option value="">---------</option>
|
||||
<option value="1" selected="selected">Mike Royko</option>
|
||||
<option value="2">Bob Woodward</option>
|
||||
</select></li>
|
||||
<li>Categories: <select multiple="multiple" name="categories">
|
||||
<option value="1">Entertainment</option>
|
||||
<option value="2">It's a test</option>
|
||||
<option value="3">Third test</option>
|
||||
</select></li>
|
||||
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1'})
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> new_art = f.apply_changes()
|
||||
>>> new_art.id
|
||||
1
|
||||
>>> new_art = Article.objects.get(id=1)
|
||||
>>> new_art.headline
|
||||
'New headline'
|
||||
"""}
|
File diff suppressed because it is too large
Load Diff
@ -40,12 +40,12 @@ def get_invalid_models():
|
||||
if f.startswith('invalid'):
|
||||
models.append((loc, f))
|
||||
return models
|
||||
|
||||
|
||||
class InvalidModelTestCase(unittest.TestCase):
|
||||
def __init__(self, model_label):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.model_label = model_label
|
||||
|
||||
|
||||
def runTest(self):
|
||||
from django.core import management
|
||||
from django.db.models.loading import load_app
|
||||
@ -55,7 +55,7 @@ class InvalidModelTestCase(unittest.TestCase):
|
||||
module = load_app(self.model_label)
|
||||
except Exception, e:
|
||||
self.fail('Unable to load invalid model module')
|
||||
|
||||
|
||||
s = StringIO()
|
||||
count = management.get_validation_errors(s, module)
|
||||
s.seek(0)
|
||||
@ -71,39 +71,43 @@ class InvalidModelTestCase(unittest.TestCase):
|
||||
|
||||
def django_tests(verbosity, tests_to_run):
|
||||
from django.conf import settings
|
||||
from django.db.models.loading import get_apps, load_app
|
||||
|
||||
old_installed_apps = settings.INSTALLED_APPS
|
||||
old_test_database_name = settings.TEST_DATABASE_NAME
|
||||
old_root_urlconf = settings.ROOT_URLCONF
|
||||
old_template_dirs = settings.TEMPLATE_DIRS
|
||||
|
||||
old_use_i18n = settings.USE_I18N
|
||||
|
||||
# Redirect some settings for the duration of these tests
|
||||
settings.TEST_DATABASE_NAME = TEST_DATABASE_NAME
|
||||
settings.INSTALLED_APPS = ALWAYS_INSTALLED_APPS
|
||||
settings.ROOT_URLCONF = 'urls'
|
||||
settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),)
|
||||
|
||||
# load all the ALWAYS_INSTALLED_APPS
|
||||
settings.USE_I18N = True
|
||||
|
||||
# Load all the ALWAYS_INSTALLED_APPS.
|
||||
# (This import statement is intentionally delayed until after we
|
||||
# access settings because of the USE_I18N dependency.)
|
||||
from django.db.models.loading import get_apps, load_app
|
||||
get_apps()
|
||||
|
||||
|
||||
# Load all the test model apps
|
||||
test_models = []
|
||||
for model_dir, model_name in get_test_models():
|
||||
for model_dir, model_name in get_test_models():
|
||||
model_label = '.'.join([model_dir, model_name])
|
||||
try:
|
||||
# if the model was named on the command line, or
|
||||
# no models were named (i.e., run all), import
|
||||
# no models were named (i.e., run all), import
|
||||
# this model and add it to the list to test.
|
||||
if not tests_to_run or model_name in tests_to_run:
|
||||
if verbosity >= 1:
|
||||
print "Importing model %s" % model_name
|
||||
mod = load_app(model_label)
|
||||
settings.INSTALLED_APPS.append(model_label)
|
||||
settings.INSTALLED_APPS.append(model_label)
|
||||
test_models.append(mod)
|
||||
except Exception, e:
|
||||
sys.stderr.write("Error while importing %s:" % model_name + ''.join(traceback.format_exception(*sys.exc_info())[1:]))
|
||||
continue
|
||||
continue
|
||||
|
||||
# Add tests for invalid models
|
||||
extra_tests = []
|
||||
@ -111,28 +115,29 @@ def django_tests(verbosity, tests_to_run):
|
||||
model_label = '.'.join([model_dir, model_name])
|
||||
if not tests_to_run or model_name in tests_to_run:
|
||||
extra_tests.append(InvalidModelTestCase(model_label))
|
||||
|
||||
|
||||
# Run the test suite, including the extra validation tests.
|
||||
from django.test.simple import run_tests
|
||||
run_tests(test_models, verbosity, extra_tests=extra_tests)
|
||||
|
||||
|
||||
# Restore the old settings
|
||||
settings.INSTALLED_APPS = old_installed_apps
|
||||
settings.TESTS_DATABASE_NAME = old_test_database_name
|
||||
settings.ROOT_URLCONF = old_root_urlconf
|
||||
settings.TEMPLATE_DIRS = old_template_dirs
|
||||
|
||||
settings.USE_I18N = old_use_i18n
|
||||
|
||||
if __name__ == "__main__":
|
||||
from optparse import OptionParser
|
||||
usage = "%prog [options] [model model model ...]"
|
||||
parser = OptionParser(usage=usage)
|
||||
parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='0',
|
||||
type='choice', choices=['0', '1', '2'],
|
||||
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
|
||||
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
|
||||
parser.add_option('--settings',
|
||||
help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
|
||||
options, args = parser.parse_args()
|
||||
if options.settings:
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
|
||||
|
||||
|
||||
django_tests(int(options.verbosity), args)
|
||||
|
Loading…
x
Reference in New Issue
Block a user