1
0
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:
Joseph Kocherhans 2006-12-29 00:25:26 +00:00
parent d87c354b65
commit a8ba8485de
47 changed files with 2920 additions and 1450 deletions

View File

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

View File

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

View File

@ -17,7 +17,7 @@ DATABASE_HOST = '' # Set to empty string for localhost. Not used wit
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
# 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:

View File

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

View File

@ -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 %}

View File

@ -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 %}

View File

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

View File

@ -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 = []

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)]

View File

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

View File

@ -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,6 +319,12 @@ def create_many_related_manager(superclass):
# *objs - objects to add
from django.db import connection
# If there aren't any objects, there is nothing to do.
if objs:
# Check that all the objects are of the right type
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])
@ -344,15 +351,19 @@ def create_many_related_manager(superclass):
# *objs - objects to remove
from django.db import connection
# If there aren't any objects, there is nothing to do.
if objs:
# Check that all the objects are of the right type
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()
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()])
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):
@ -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,

View File

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

View File

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

View File

@ -0,0 +1 @@
from widgets import *

View 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

View File

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

View File

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

View File

@ -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})

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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'"

View File

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

View File

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

View File

@ -22,6 +22,9 @@ of the community, so there are many ways you can help Django's development:
likely to be skeptical of large-scale suggestions without some code to
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
=================

View File

@ -1,5 +1,5 @@
=====================================
Cross Site Request Forgery Protection
Cross Site Request Forgery protection
=====================================
The CsrfMiddleware class provides easy-to-use protection against
@ -12,11 +12,11 @@ The first defense against CSRF attacks is to ensure that GET requests
are side-effect free. POST requests can then be protected by adding this
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
your list of middleware classes, ``MIDDLEWARE_CLASSES``. It needs to process
the response after the SessionMiddleware, so must come before it in the
@ -25,6 +25,7 @@ happen to the response, so it must come after GZipMiddleware in the list.
How it works
============
CsrfMiddleware does two things:
1. It modifies outgoing requests by adding a hidden form field to all
@ -55,6 +56,7 @@ 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.
@ -65,4 +67,3 @@ you might bypass the filter that adds the hidden field to the form,
in which case form submission will always fail. It may still be possible
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.

View File

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

View File

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

View File

@ -902,7 +902,7 @@ If ``template_name`` isn't specified, this view will use the template
In addition to ``extra_context``, the template's context will be:
* ``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.

View File

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

View File

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

View File

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

View File

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

View File

@ -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',

View File

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

View File

@ -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()
[]
"""}

View File

View 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&#39;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&#39;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

View File

@ -71,20 +71,24 @@ 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),)
settings.USE_I18N = True
# load all the ALWAYS_INSTALLED_APPS
# Load all the ALWAYS_INSTALLED_APPS.
# (This import statement is intentionally delayed until after we
# access settings because of the USE_I18N dependency.)
from django.db.models.loading import get_apps, load_app
get_apps()
# Load all the test model apps
@ -121,6 +125,7 @@ def django_tests(verbosity, tests_to_run):
settings.TESTS_DATABASE_NAME = old_test_database_name
settings.ROOT_URLCONF = old_root_urlconf
settings.TEMPLATE_DIRS = old_template_dirs
settings.USE_I18N = old_use_i18n
if __name__ == "__main__":
from optparse import OptionParser