1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

boulder-oracle-sprint: Merged to trunk [4455].

git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@4456 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Boulder Sprinters 2007-02-02 17:35:55 +00:00
parent 92b7851424
commit e17f755514
78 changed files with 2389 additions and 592 deletions

View File

@ -25,7 +25,7 @@
</p> </p>
{% endif %} {% endif %}
<p>{% blocktrans with original.username|escape as username %}Enter a new username and password for the user <strong>{{ username }}</strong>.{% endblocktrans %}</p> <p>{% blocktrans with original.username|escape as username %}Enter a new password for the user <strong>{{ username }}</strong>.{% endblocktrans %}</p>
<fieldset class="module aligned"> <fieldset class="module aligned">

View File

@ -101,6 +101,10 @@ def result_headers(cl):
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')} "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
def _boolean_icon(field_val):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
return '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
def items_for_result(cl, result): def items_for_result(cl, result):
first = True first = True
pk = cl.lookup_opts.pk.attname pk = cl.lookup_opts.pk.attname
@ -114,8 +118,13 @@ def items_for_result(cl, result):
try: try:
attr = getattr(result, field_name) attr = getattr(result, field_name)
allow_tags = getattr(attr, 'allow_tags', False) allow_tags = getattr(attr, 'allow_tags', False)
boolean = getattr(attr, 'boolean', False)
if callable(attr): if callable(attr):
attr = attr() attr = attr()
if boolean:
allow_tags = True
result_repr = _boolean_icon(attr)
else:
result_repr = str(attr) result_repr = str(attr)
except (AttributeError, ObjectDoesNotExist): except (AttributeError, ObjectDoesNotExist):
result_repr = EMPTY_CHANGELIST_VALUE result_repr = EMPTY_CHANGELIST_VALUE
@ -147,8 +156,7 @@ def items_for_result(cl, result):
row_class = ' class="nowrap"' row_class = ' class="nowrap"'
# Booleans are special: We use images. # Booleans are special: We use images.
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField): elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} result_repr = _boolean_icon(field_val)
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
# FloatFields are special: Zero-pad the decimals. # FloatFields are special: Zero-pad the decimals.
elif isinstance(f, models.FloatField): elif isinstance(f, models.FloatField):
if field_val is not None: if field_val is not None:

View File

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
CONTENT_TYPE_CACHE = {}
class ContentTypeManager(models.Manager): class ContentTypeManager(models.Manager):
def get_for_model(self, model): def get_for_model(self, model):
""" """
@ -8,10 +9,15 @@ class ContentTypeManager(models.Manager):
ContentType if necessary. ContentType if necessary.
""" """
opts = model._meta opts = model._meta
key = (opts.app_label, opts.object_name.lower())
try:
ct = CONTENT_TYPE_CACHE[key]
except KeyError:
# The str() is needed around opts.verbose_name because it's a # The str() is needed around opts.verbose_name because it's a
# django.utils.functional.__proxy__ object. # django.utils.functional.__proxy__ object.
ct, created = self.model._default_manager.get_or_create(app_label=opts.app_label, ct, created = self.model._default_manager.get_or_create(app_label=key[0],
model=opts.object_name.lower(), defaults={'name': str(opts.verbose_name)}) model=key[1], defaults={'name': str(opts.verbose_name)})
CONTENT_TYPE_CACHE[key] = ct
return ct return ct
class ContentType(models.Model): class ContentType(models.Model):

View File

@ -1,5 +1,6 @@
from django.conf import settings from django.conf import settings
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from django.core.exceptions import SuspiciousOperation
from django.utils.cache import patch_vary_headers from django.utils.cache import patch_vary_headers
import datetime import datetime
@ -55,7 +56,7 @@ class SessionWrapper(object):
s = Session.objects.get(session_key=self.session_key, s = Session.objects.get(session_key=self.session_key,
expire_date__gt=datetime.datetime.now()) expire_date__gt=datetime.datetime.now())
self._session_cache = s.get_decoded() self._session_cache = s.get_decoded()
except Session.DoesNotExist: except (Session.DoesNotExist, SuspiciousOperation):
self._session_cache = {} self._session_cache = {}
# Set the session_key to None to force creation of a new # Set the session_key to None to force creation of a new
# key, for extra security. # key, for extra security.

View File

@ -6,8 +6,8 @@ class CacheClass(BaseCache):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
pass pass
def get(self, *args, **kwargs): def get(self, key, default=None):
pass return default
def set(self, *args, **kwargs): def set(self, *args, **kwargs):
pass pass
@ -16,7 +16,7 @@ class CacheClass(BaseCache):
pass pass
def get_many(self, *args, **kwargs): def get_many(self, *args, **kwargs):
pass return {}
def has_key(self, *args, **kwargs): def has_key(self, *args, **kwargs):
return False return False

View File

@ -25,7 +25,7 @@ APP_ARGS = '[appname ...]'
# which has been installed. # which has been installed.
PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template') PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
INVALID_PROJECT_NAMES = ('django', 'test') INVALID_PROJECT_NAMES = ('django', 'site', 'test')
# Set up the terminal color scheme. # Set up the terminal color scheme.
class dummy: pass class dummy: pass
@ -735,7 +735,7 @@ def startproject(project_name, directory):
"Creates a Django project for the given project_name in the given directory." "Creates a Django project for the given project_name in the given directory."
from random import choice from random import choice
if project_name in INVALID_PROJECT_NAMES: if project_name in INVALID_PROJECT_NAMES:
sys.stderr.write(style.ERROR("Error: %r isn't a valid project name. Please try another.\n" % project_name)) sys.stderr.write(style.ERROR("Error: '%r' conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name.\n" % project_name))
sys.exit(1) sys.exit(1)
_start_helper('project', project_name, directory) _start_helper('project', project_name, directory)
# Create a random SECRET_KEY hash, and put it in the main settings. # Create a random SECRET_KEY hash, and put it in the main settings.

View File

@ -57,7 +57,7 @@ def Deserializer(object_list, **options):
for d in object_list: for d in object_list:
# Look up the model and starting build a dict of data for it. # Look up the model and starting build a dict of data for it.
Model = _get_model(d["model"]) Model = _get_model(d["model"])
data = {Model._meta.pk.name : d["pk"]} data = {Model._meta.pk.attname : d["pk"]}
m2m_data = {} m2m_data = {}
# Handle each field # Handle each field

View File

@ -21,6 +21,5 @@ DATA_TYPES = {
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'text', 'TextField': 'text',
'TimeField': 'time', 'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)', 'USStateField': 'varchar(2)',
} }

View File

@ -25,6 +25,5 @@ DATA_TYPES = {
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'longtext', 'TextField': 'longtext',
'TimeField': 'time', 'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)', 'USStateField': 'varchar(2)',
} }

View File

@ -25,6 +25,5 @@ DATA_TYPES = {
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'text', 'TextField': 'text',
'TimeField': 'time', 'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)', 'USStateField': 'varchar(2)',
} }

View File

@ -24,6 +24,5 @@ DATA_TYPES = {
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'text', 'TextField': 'text',
'TimeField': 'time', 'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)', 'USStateField': 'varchar(2)',
} }

View File

@ -337,11 +337,11 @@ class Field(object):
return self._choices return self._choices
choices = property(_get_choices) choices = property(_get_choices)
def formfield(self, initial=None): def formfield(self, **kwargs):
"Returns a django.newforms.Field instance for this database Field." "Returns a django.newforms.Field instance for this database Field."
from django.newforms import CharField defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
# TODO: This is just a temporary default during development. defaults.update(kwargs)
return forms.CharField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) return forms.CharField(**defaults)
def value_from_object(self, obj): def value_from_object(self, obj):
"Returns the value of this field in the given model instance." "Returns the value of this field in the given model instance."
@ -383,7 +383,7 @@ class AutoField(Field):
super(AutoField, self).contribute_to_class(cls, name) super(AutoField, self).contribute_to_class(cls, name)
cls._meta.has_auto_field = True cls._meta.has_auto_field = True
def formfield(self, initial=None): def formfield(self, **kwargs):
return None return None
class BooleanField(Field): class BooleanField(Field):
@ -400,8 +400,10 @@ class BooleanField(Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [oldforms.CheckboxField] return [oldforms.CheckboxField]
def formfield(self, initial=None): def formfield(self, **kwargs):
return forms.BooleanField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.BooleanField(**defaults)
class CharField(Field): class CharField(Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
@ -417,8 +419,10 @@ class CharField(Field):
raise validators.ValidationError, gettext_lazy("This field cannot be null.") raise validators.ValidationError, gettext_lazy("This field cannot be null.")
return str(value) return str(value)
def formfield(self, initial=None): def formfield(self, **kwargs):
return forms.CharField(max_length=self.maxlength, required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'max_length': self.maxlength, 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.CharField(**defaults)
# TODO: Maybe move this into contrib, because it's specialized. # TODO: Maybe move this into contrib, because it's specialized.
class CommaSeparatedIntegerField(CharField): class CommaSeparatedIntegerField(CharField):
@ -497,8 +501,10 @@ class DateField(Field):
val = self._get_val_from_obj(obj) val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')} return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
def formfield(self, initial=None): def formfield(self, **kwargs):
return forms.DateField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.DateField(**defaults)
class DateTimeField(DateField): class DateTimeField(DateField):
def to_python(self, value): def to_python(self, value):
@ -569,8 +575,10 @@ class DateTimeField(DateField):
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
time_field: (val is not None and val.strftime("%H:%M:%S") or '')} time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
def formfield(self, initial=None): def formfield(self, **kwargs):
return forms.DateTimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.DateTimeField(**defaults)
class EmailField(CharField): class EmailField(CharField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -586,8 +594,10 @@ class EmailField(CharField):
def validate(self, field_data, all_data): def validate(self, field_data, all_data):
validators.isValidEmail(field_data, all_data) validators.isValidEmail(field_data, all_data)
def formfield(self, initial=None): def formfield(self, **kwargs):
return forms.EmailField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.EmailField(**defaults)
class FileField(Field): class FileField(Field):
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs): def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
@ -721,8 +731,10 @@ class IntegerField(Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [oldforms.IntegerField] return [oldforms.IntegerField]
def formfield(self, initial=None): def formfield(self, **kwargs):
return forms.IntegerField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.IntegerField(**defaults)
class IPAddressField(Field): class IPAddressField(Field):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -778,6 +790,11 @@ class TextField(Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [oldforms.LargeTextField] return [oldforms.LargeTextField]
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'widget': forms.Textarea, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.CharField(**defaults)
class TimeField(Field): class TimeField(Field):
empty_strings_allowed = False empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs): def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
@ -824,21 +841,29 @@ class TimeField(Field):
val = self._get_val_from_obj(obj) val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')} return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
def formfield(self, initial=None): def formfield(self, **kwargs):
return forms.TimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.TimeField(**defaults)
class URLField(Field): class URLField(CharField):
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
kwargs['maxlength'] = kwargs.get('maxlength', 200)
if verify_exists: if verify_exists:
kwargs.setdefault('validator_list', []).append(validators.isExistingURL) kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
self.verify_exists = verify_exists self.verify_exists = verify_exists
Field.__init__(self, verbose_name, name, **kwargs) CharField.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
return [oldforms.URLField] return [oldforms.URLField]
def formfield(self, initial=None): def get_internal_type(self):
return forms.URLField(required=not self.blank, verify_exists=self.verify_exists, label=capfirst(self.verbose_name), initial=initial) return "CharField"
def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'verify_exists': self.verify_exists, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.URLField(**defaults)
class USStateField(Field): class USStateField(Field):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):

View File

@ -316,18 +316,20 @@ def create_many_related_manager(superclass):
# join_table: name of the m2m link table # join_table: name of the m2m link table
# source_col_name: the PK colname in join_table for the source object # source_col_name: the PK colname in join_table for the source object
# target_col_name: the PK colname in join_table for the target object # target_col_name: the PK colname in join_table for the target object
# *objs - objects to add # *objs - objects to add. Either object instances, or primary keys of object instances.
from django.db import connection from django.db import connection
# If there aren't any objects, there is nothing to do. # If there aren't any objects, there is nothing to do.
if objs: if objs:
# Check that all the objects are of the right type # Check that all the objects are of the right type
new_ids = set()
for obj in objs: for obj in objs:
if not isinstance(obj, self.model): if isinstance(obj, self.model):
raise ValueError, "objects to add() must be %s instances" % self.model._meta.object_name new_ids.add(obj._get_pk_val())
else:
new_ids.add(obj)
# Add the newly created or already existing objects to the join table. # Add the newly created or already existing objects to the join table.
# First find out which items are already added, to avoid adding them twice # First find out which items are already added, to avoid adding them twice
new_ids = set([obj._get_pk_val() for obj in objs])
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \ cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
(target_col_name, self.join_table, source_col_name, (target_col_name, self.join_table, source_col_name,
@ -354,11 +356,13 @@ def create_many_related_manager(superclass):
# If there aren't any objects, there is nothing to do. # If there aren't any objects, there is nothing to do.
if objs: if objs:
# Check that all the objects are of the right type # Check that all the objects are of the right type
old_ids = set()
for obj in objs: for obj in objs:
if not isinstance(obj, self.model): if isinstance(obj, self.model):
raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name old_ids.add(obj._get_pk_val())
else:
old_ids.add(obj)
# Remove the specified objects from the join table # Remove the specified objects from the join table
old_ids = set([obj._get_pk_val() for obj in objs])
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \ cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
(self.join_table, source_col_name, (self.join_table, source_col_name,
@ -548,8 +552,10 @@ class ForeignKey(RelatedField, Field):
def contribute_to_related_class(self, cls, related): def contribute_to_related_class(self, cls, related):
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related)) setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
def formfield(self, initial=None): def formfield(self, **kwargs):
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.ChoiceField(**defaults)
class OneToOneField(RelatedField, IntegerField): class OneToOneField(RelatedField, IntegerField):
def __init__(self, to, to_field=None, **kwargs): def __init__(self, to, to_field=None, **kwargs):
@ -612,8 +618,10 @@ class OneToOneField(RelatedField, IntegerField):
if not cls._meta.one_to_one_field: if not cls._meta.one_to_one_field:
cls._meta.one_to_one_field = self cls._meta.one_to_one_field = self
def formfield(self, initial=None): def formfield(self, **kwargs):
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.ChoiceField(**kwargs)
class ManyToManyField(RelatedField, Field): class ManyToManyField(RelatedField, Field):
def __init__(self, to, **kwargs): def __init__(self, to, **kwargs):
@ -625,6 +633,7 @@ class ManyToManyField(RelatedField, Field):
limit_choices_to=kwargs.pop('limit_choices_to', None), limit_choices_to=kwargs.pop('limit_choices_to', None),
raw_id_admin=kwargs.pop('raw_id_admin', False), raw_id_admin=kwargs.pop('raw_id_admin', False),
symmetrical=kwargs.pop('symmetrical', True)) symmetrical=kwargs.pop('symmetrical', True))
self.db_table = kwargs.pop('db_table', None)
if kwargs["rel"].raw_id_admin: if kwargs["rel"].raw_id_admin:
kwargs.setdefault("validator_list", []).append(self.isValidIDList) kwargs.setdefault("validator_list", []).append(self.isValidIDList)
Field.__init__(self, **kwargs) Field.__init__(self, **kwargs)
@ -647,10 +656,10 @@ class ManyToManyField(RelatedField, Field):
def _get_m2m_db_table(self, opts): def _get_m2m_db_table(self, opts):
"Function that can be curried to provide the m2m table name for this relation" "Function that can be curried to provide the m2m table name for this relation"
from django.db import backend if self.db_table:
from django.db.backends.util import truncate_name return self.db_table
name = '%s_%s' % (opts.db_table, self.name) else:
return truncate_name(name, backend.get_max_name_length()) return '%s_%s' % (opts.db_table, self.name)
def _get_m2m_column_name(self, related): def _get_m2m_column_name(self, related):
"Function that can be curried to provide the source column name for the m2m table" "Function that can be curried to provide the source column name for the m2m table"
@ -728,12 +737,14 @@ class ManyToManyField(RelatedField, Field):
"Returns the value of this field in the given model instance." "Returns the value of this field in the given model instance."
return getattr(obj, self.attname).all() return getattr(obj, self.attname).all()
def formfield(self, initial=None): def formfield(self, **kwargs):
# If initial is passed in, it's a list of related objects, but the # If initial is passed in, it's a list of related objects, but the
# MultipleChoiceField takes a list of IDs. # MultipleChoiceField takes a list of IDs.
if initial is not None: if kwargs.get('initial') is not None:
initial = [i._get_pk_val() for i in initial] kwargs['initial'] = [i._get_pk_val() for i in kwargs['initial']]
return forms.MultipleChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial) defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
defaults.update(kwargs)
return forms.MultipleChoiceField(**defaults)
class ManyToOneRel(object): class ManyToOneRel(object):
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None, def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,

View File

@ -1,4 +1,4 @@
from django.db.models.query import QuerySet from django.db.models.query import QuerySet, EmptyQuerySet
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.db.models import signals from django.db.models import signals
from django.db.models.fields import FieldDoesNotExist from django.db.models.fields import FieldDoesNotExist
@ -42,12 +42,18 @@ class Manager(object):
# PROXIES TO QUERYSET # # PROXIES TO QUERYSET #
####################### #######################
def get_empty_query_set(self):
return EmptyQuerySet(self.model)
def get_query_set(self): def get_query_set(self):
"""Returns a new QuerySet object. Subclasses can override this method """Returns a new QuerySet object. Subclasses can override this method
to easily customise the behaviour of the Manager. to easily customise the behaviour of the Manager.
""" """
return QuerySet(self.model) return QuerySet(self.model)
def none(self):
return self.get_empty_query_set()
def all(self): def all(self):
return self.get_query_set() return self.get_query_set()

View File

@ -1,5 +1,6 @@
from django.db import backend, connection, get_query_module, transaction from django.db import backend, connection, get_query_module, transaction
from django.db.models.fields import DateField, FieldDoesNotExist from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models.fields.generic import GenericRelation
from django.db.models import signals from django.db.models import signals
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
@ -25,6 +26,9 @@ QUERY_TERMS = (
# Larger values are slightly faster at the expense of more storage space. # Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100 GET_ITERATOR_CHUNK_SIZE = 100
class EmptyResultSet(Exception):
pass
#################### ####################
# HELPER FUNCTIONS # # HELPER FUNCTIONS #
#################### ####################
@ -169,7 +173,11 @@ class _QuerySet(object):
cursor = connection.cursor() cursor = connection.cursor()
try:
select, sql, params, full_query = self._get_sql_clause() select, sql, params, full_query = self._get_sql_clause()
except EmptyResultSet:
raise StopIteration
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
fill_cache = self._select_related fill_cache = self._select_related
@ -194,7 +202,12 @@ class _QuerySet(object):
counter._offset = None counter._offset = None
counter._limit = None counter._limit = None
counter._select_related = False counter._select_related = False
try:
select, sql, params, full_query = counter._get_sql_clause() select, sql, params, full_query = counter._get_sql_clause()
except EmptyResultSet:
return 0
cursor = connection.cursor() cursor = connection.cursor()
if self._distinct: if self._distinct:
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table), id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
@ -532,7 +545,12 @@ class ValuesQuerySet(QuerySet):
field_names = [f.attname for f in self.model._meta.fields] field_names = [f.attname for f in self.model._meta.fields]
cursor = connection.cursor() cursor = connection.cursor()
try:
select, sql, params, full_query = self._get_sql_clause() select, sql, params, full_query = self._get_sql_clause()
except EmptyResultSet:
raise StopIteration
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
while 1: while 1:
@ -554,19 +572,25 @@ class DateQuerySet(QuerySet):
if self._field.null: if self._field.null:
self._where.append('%s.%s IS NOT NULL' % \ self._where.append('%s.%s IS NOT NULL' % \
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
try:
select, sql, params, full_query = self._get_sql_clause() select, sql, params, full_query = self._get_sql_clause()
except EmptyResultSet:
raise StopIteration
table_name = backend.quote_name(self.model._meta.db_table) table_name = backend.quote_name(self.model._meta.db_table)
field_name = backend.quote_name(self._field.column) field_name = backend.quote_name(self._field.column)
date_trunc_sql = backend.get_date_trunc_sql(self._kind,
'%s.%s' % (table_name, field_name))
if backend.allows_group_by_ordinal: if backend.allows_group_by_ordinal:
group_by = '1' group_by = '1'
else: else:
group_by = date_trunc_sql group_by = backend.get_date_trunc_sql(self._kind,
fmt = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' '%s.%s' % (table_name, field_name))
stmt = fmt % (date_trunc_sql, sql, group_by, self._order)
sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \
(backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
backend.quote_name(self._field.column))), sql, group_by, self._order)
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute(stmt, params) cursor.execute(sql, params)
if backend.needs_datetime_string_cast: if backend.needs_datetime_string_cast:
return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
else: else:
@ -579,6 +603,25 @@ class DateQuerySet(QuerySet):
c._order = self._order c._order = self._order
return c return c
class EmptyQuerySet(QuerySet):
def __init__(self, model=None):
super(EmptyQuerySet, self).__init__(model)
self._result_cache = []
def iterator(self):
raise StopIteration
def count(self):
return 0
def delete(self):
pass
def _clone(self, klass=None, **kwargs):
c = super(EmptyQuerySet, self)._clone(klass, **kwargs)
c._result_cache = []
return c
class QOperator(object): class QOperator(object):
"Base class for QAnd and QOr" "Base class for QAnd and QOr"
def __init__(self, *args): def __init__(self, *args):
@ -587,10 +630,14 @@ class QOperator(object):
def get_sql(self, opts): def get_sql(self, opts):
joins, where, params = SortedDict(), [], [] joins, where, params = SortedDict(), [], []
for val in self.args: for val in self.args:
try:
joins2, where2, params2 = val.get_sql(opts) joins2, where2, params2 = val.get_sql(opts)
joins.update(joins2) joins.update(joins2)
where.extend(where2) where.extend(where2)
params.extend(params2) params.extend(params2)
except EmptyResultSet:
if not isinstance(self, QOr):
raise EmptyResultSet
if where: if where:
return joins, ['(%s)' % self.operator.join(where)], params return joins, ['(%s)' % self.operator.join(where)], params
return joins, [], params return joins, [], params
@ -644,8 +691,11 @@ class QNot(Q):
self.q = q self.q = q
def get_sql(self, opts): def get_sql(self, opts):
try:
joins, where, params = self.q.get_sql(opts) joins, where, params = self.q.get_sql(opts)
where2 = ['(NOT (%s))' % " AND ".join(where)] where2 = ['(NOT (%s))' % " AND ".join(where)]
except EmptyResultSet:
return SortedDict(), [], []
return joins, where2, params return joins, where2, params
def get_where_clause(lookup_type, table_prefix, field_name, value): def get_where_clause(lookup_type, table_prefix, field_name, value):
@ -662,7 +712,11 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
except KeyError: except KeyError:
pass pass
if lookup_type == 'in': if lookup_type == 'in':
return '%s%s IN (%s)' % (table_prefix, field_name, ','.join(['%s' for v in value])) in_string = ','.join(['%s' for id in value])
if in_string:
return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
else:
raise EmptyResultSet
elif lookup_type == 'range': elif lookup_type == 'range':
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name) return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
elif lookup_type in ('year', 'month', 'day'): elif lookup_type in ('year', 'month', 'day'):
@ -948,6 +1002,7 @@ def delete_objects(seen_objs):
pk_list = [pk for pk,instance in seen_objs[cls]] pk_list = [pk for pk,instance in seen_objs[cls]]
for related in cls._meta.get_all_related_many_to_many_objects(): for related in cls._meta.get_all_related_many_to_many_objects():
if not isinstance(related.field, GenericRelation):
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(related.field.m2m_db_table()), (qn(related.field.m2m_db_table()),
@ -955,11 +1010,18 @@ def delete_objects(seen_objs):
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
for f in cls._meta.many_to_many: for f in cls._meta.many_to_many:
if isinstance(f, GenericRelation):
from django.contrib.contenttypes.models import ContentType
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
args_extra = [ContentType.objects.get_for_model(cls).id]
else:
query_extra = ''
args_extra = []
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(f.m2m_db_table()), qn(f.m2m_column_name()), (qn(f.m2m_db_table()), qn(f.m2m_column_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra,
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
for field in cls._meta.fields: for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_objs: if field.rel and field.null and field.rel.to in seen_objs:
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):

View File

@ -3,8 +3,8 @@ Field classes
""" """
from django.utils.translation import gettext from django.utils.translation import gettext
from util import ValidationError, smart_unicode from util import ErrorList, ValidationError, smart_unicode
from widgets import TextInput, PasswordInput, CheckboxInput, Select, SelectMultiple from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
import datetime import datetime
import re import re
import time import time
@ -15,8 +15,9 @@ __all__ = (
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField', 'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
'RegexField', 'EmailField', 'URLField', 'BooleanField', 'RegexField', 'EmailField', 'URLField', 'BooleanField',
'ChoiceField', 'MultipleChoiceField', 'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
'ComboField', 'ComboField', 'MultiValueField',
'SplitDateTimeField',
) )
# These values, if given to to_python(), will trigger the self.required check. # These values, if given to to_python(), will trigger the self.required check.
@ -29,11 +30,12 @@ except NameError:
class Field(object): class Field(object):
widget = TextInput # Default widget to use when rendering this type of Field. widget = TextInput # Default widget to use when rendering this type of Field.
hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
# Tracks each time a Field instance is created. Used to retain order. # Tracks each time a Field instance is created. Used to retain order.
creation_counter = 0 creation_counter = 0
def __init__(self, required=True, widget=None, label=None, initial=None): def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None):
# required -- Boolean that specifies whether the field is required. # required -- Boolean that specifies whether the field is required.
# True by default. # True by default.
# widget -- A Widget class, or instance of a Widget class, that should be # widget -- A Widget class, or instance of a Widget class, that should be
@ -45,9 +47,11 @@ class Field(object):
# field name, if the Field is part of a 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 # initial -- A value to use in this Field's initial display. This value is
# *not* used as a fallback if data isn't given. # *not* used as a fallback if data isn't given.
# help_text -- An optional string to use as "help text" for this Field.
if label is not None: if label is not None:
label = smart_unicode(label) label = smart_unicode(label)
self.required, self.label, self.initial = required, label, initial self.required, self.label, self.initial = required, label, initial
self.help_text = help_text
widget = widget or self.widget widget = widget or self.widget
if isinstance(widget, type): if isinstance(widget, type):
widget = widget() widget = widget()
@ -83,17 +87,15 @@ class Field(object):
return {} return {}
class CharField(Field): class CharField(Field):
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None, initial=None): def __init__(self, max_length=None, min_length=None, *args, **kwargs):
self.max_length, self.min_length = max_length, min_length self.max_length, self.min_length = max_length, min_length
Field.__init__(self, required, widget, label, initial) super(CharField, self).__init__(*args, **kwargs)
def clean(self, value): def clean(self, value):
"Validates max_length and min_length. Returns a Unicode object." "Validates max_length and min_length. Returns a Unicode object."
Field.clean(self, value) super(CharField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
value = u'' return u''
if not self.required:
return value
value = smart_unicode(value) value = smart_unicode(value)
if self.max_length is not None and len(value) > self.max_length: if self.max_length is not None and len(value) > self.max_length:
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length) raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
@ -106,18 +108,18 @@ class CharField(Field):
return {'maxlength': str(self.max_length)} return {'maxlength': str(self.max_length)}
class IntegerField(Field): class IntegerField(Field):
def __init__(self, max_value=None, min_value=None, required=True, widget=None, label=None, initial=None): def __init__(self, max_value=None, min_value=None, *args, **kwargs):
self.max_value, self.min_value = max_value, min_value self.max_value, self.min_value = max_value, min_value
Field.__init__(self, required, widget, label, initial) super(IntegerField, self).__init__(*args, **kwargs)
def clean(self, value): def clean(self, value):
""" """
Validates that int() can be called on the input. Returns the result Validates that int() can be called on the input. Returns the result
of int(). of int(). Returns None for empty values.
""" """
super(IntegerField, self).clean(value) super(IntegerField, self).clean(value)
if not self.required and value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return None
try: try:
value = int(value) value = int(value)
except (ValueError, TypeError): except (ValueError, TypeError):
@ -137,8 +139,8 @@ DEFAULT_DATE_INPUT_FORMATS = (
) )
class DateField(Field): class DateField(Field):
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None): def __init__(self, input_formats=None, *args, **kwargs):
Field.__init__(self, required, widget, label, initial) super(DateField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
def clean(self, value): def clean(self, value):
@ -146,7 +148,7 @@ class DateField(Field):
Validates that the input can be converted to a date. Returns a Python Validates that the input can be converted to a date. Returns a Python
datetime.date object. datetime.date object.
""" """
Field.clean(self, value) super(DateField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return None return None
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
@ -166,8 +168,8 @@ DEFAULT_TIME_INPUT_FORMATS = (
) )
class TimeField(Field): class TimeField(Field):
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None): def __init__(self, input_formats=None, *args, **kwargs):
Field.__init__(self, required, widget, label, initial) super(TimeField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
def clean(self, value): def clean(self, value):
@ -175,7 +177,7 @@ class TimeField(Field):
Validates that the input can be converted to a time. Returns a Python Validates that the input can be converted to a time. Returns a Python
datetime.time object. datetime.time object.
""" """
Field.clean(self, value) super(TimeField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return None return None
if isinstance(value, datetime.time): if isinstance(value, datetime.time):
@ -200,8 +202,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
) )
class DateTimeField(Field): class DateTimeField(Field):
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None): def __init__(self, input_formats=None, *args, **kwargs):
Field.__init__(self, required, widget, label, initial) super(DateTimeField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
def clean(self, value): def clean(self, value):
@ -209,7 +211,7 @@ class DateTimeField(Field):
Validates that the input can be converted to a datetime. Returns a Validates that the input can be converted to a datetime. Returns a
Python datetime.datetime object. Python datetime.datetime object.
""" """
Field.clean(self, value) super(DateTimeField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return None return None
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
@ -224,14 +226,13 @@ class DateTimeField(Field):
raise ValidationError(gettext(u'Enter a valid date/time.')) raise ValidationError(gettext(u'Enter a valid date/time.'))
class RegexField(Field): class RegexField(Field):
def __init__(self, regex, max_length=None, min_length=None, error_message=None, def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
required=True, widget=None, label=None, initial=None):
""" """
regex can be either a string or a compiled regular expression object. regex can be either a string or a compiled regular expression object.
error_message is an optional error message to use, if error_message is an optional error message to use, if
'Enter a valid value' is too generic for you. 'Enter a valid value' is too generic for you.
""" """
Field.__init__(self, required, widget, label, initial) super(RegexField, self).__init__(*args, **kwargs)
if isinstance(regex, basestring): if isinstance(regex, basestring):
regex = re.compile(regex) regex = re.compile(regex)
self.regex = regex self.regex = regex
@ -243,10 +244,11 @@ class RegexField(Field):
Validates that the input matches the regular expression. Returns a Validates that the input matches the regular expression. Returns a
Unicode object. Unicode object.
""" """
Field.clean(self, value) super(RegexField, self).clean(value)
if value in EMPTY_VALUES: value = u'' if value in EMPTY_VALUES:
value = u''
value = smart_unicode(value) value = smart_unicode(value)
if not self.required and value == u'': if value == u'':
return value return value
if self.max_length is not None and len(value) > self.max_length: if self.max_length is not None and len(value) > self.max_length:
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length) raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
@ -262,8 +264,9 @@ email_re = re.compile(
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
class EmailField(RegexField): class EmailField(RegexField):
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None, initial=None): def __init__(self, max_length=None, min_length=None, *args, **kwargs):
RegexField.__init__(self, email_re, max_length, min_length, gettext(u'Enter a valid e-mail address.'), required, widget, label, initial) RegexField.__init__(self, email_re, max_length, min_length,
gettext(u'Enter a valid e-mail address.'), *args, **kwargs)
url_re = re.compile( url_re = re.compile(
r'^https?://' # http:// or https:// r'^https?://' # http:// or https://
@ -279,14 +282,16 @@ except ImportError:
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)' URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
class URLField(RegexField): class URLField(RegexField):
def __init__(self, max_length=None, min_length=None, required=True, verify_exists=False, widget=None, label=None, def __init__(self, max_length=None, min_length=None, verify_exists=False,
initial=None, validator_user_agent=URL_VALIDATOR_USER_AGENT): validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
RegexField.__init__(self, url_re, max_length, min_length, gettext(u'Enter a valid URL.'), required, widget, label, initial) super(URLField, self).__init__(url_re, max_length, min_length, gettext(u'Enter a valid URL.'), *args, **kwargs)
self.verify_exists = verify_exists self.verify_exists = verify_exists
self.user_agent = validator_user_agent self.user_agent = validator_user_agent
def clean(self, value): def clean(self, value):
value = RegexField.clean(self, value) value = super(URLField, self).clean(value)
if value == u'':
return value
if self.verify_exists: if self.verify_exists:
import urllib2 import urllib2
from django.conf import settings from django.conf import settings
@ -311,24 +316,43 @@ class BooleanField(Field):
def clean(self, value): def clean(self, value):
"Returns a Python boolean object." "Returns a Python boolean object."
Field.clean(self, value) super(BooleanField, self).clean(value)
return bool(value) return bool(value)
class NullBooleanField(BooleanField):
"""
A field whose valid values are None, True and False. Invalid values are
cleaned to None.
"""
widget = NullBooleanSelect
def clean(self, value):
return {True: True, False: False}.get(value, None)
class ChoiceField(Field): class ChoiceField(Field):
def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None): def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None, help_text=None):
if isinstance(widget, type): super(ChoiceField, self).__init__(required, widget, label, initial, help_text)
widget = widget(choices=choices)
Field.__init__(self, required, widget, label, initial)
self.choices = choices self.choices = choices
def _get_choices(self):
return self._choices
def _set_choices(self, value):
# Setting choices also sets the choices on the widget.
self._choices = value
self.widget.choices = value
choices = property(_get_choices, _set_choices)
def clean(self, value): def clean(self, value):
""" """
Validates that the input is in self.choices. Validates that the input is in self.choices.
""" """
value = Field.clean(self, value) value = super(ChoiceField, self).clean(value)
if value in EMPTY_VALUES: value = u'' if value in EMPTY_VALUES:
value = u''
value = smart_unicode(value) value = smart_unicode(value)
if not self.required and value == u'': if value == u'':
return value return value
valid_values = set([str(k) for k, v in self.choices]) valid_values = set([str(k) for k, v in self.choices])
if value not in valid_values: if value not in valid_values:
@ -336,8 +360,10 @@ class ChoiceField(Field):
return value return value
class MultipleChoiceField(ChoiceField): class MultipleChoiceField(ChoiceField):
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None): hidden_widget = MultipleHiddenInput
ChoiceField.__init__(self, choices, required, widget, label, initial)
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None, help_text=None):
super(MultipleChoiceField, self).__init__(choices, required, widget, label, initial, help_text)
def clean(self, value): def clean(self, value):
""" """
@ -361,8 +387,11 @@ class MultipleChoiceField(ChoiceField):
return new_value return new_value
class ComboField(Field): class ComboField(Field):
def __init__(self, fields=(), required=True, widget=None, label=None, initial=None): """
Field.__init__(self, required, widget, label, initial) A Field whose clean() method calls multiple Field clean() methods.
"""
def __init__(self, fields=(), *args, **kwargs):
super(ComboField, self).__init__(*args, **kwargs)
# Set 'required' to False on the individual fields, because the # Set 'required' to False on the individual fields, because the
# required validation will be handled by ComboField, not by those # required validation will be handled by ComboField, not by those
# individual fields. # individual fields.
@ -375,7 +404,88 @@ class ComboField(Field):
Validates the given value against all of self.fields, which is a Validates the given value against all of self.fields, which is a
list of Field instances. list of Field instances.
""" """
Field.clean(self, value) super(ComboField, self).clean(value)
for field in self.fields: for field in self.fields:
value = field.clean(value) value = field.clean(value)
return value return value
class MultiValueField(Field):
"""
A Field that is composed of multiple Fields.
Its clean() method takes a "decompressed" list of values. Each value in
this list is cleaned by the corresponding field -- the first value is
cleaned by the first field, the second value is cleaned by the second
field, etc. Once all fields are cleaned, the list of clean values is
"compressed" into a single value.
Subclasses should implement compress(), which specifies how a list of
valid values should be converted to a single value. Subclasses should not
have to implement clean().
You'll probably want to use this with MultiWidget.
"""
def __init__(self, fields=(), *args, **kwargs):
super(MultiValueField, self).__init__(*args, **kwargs)
# Set 'required' to False on the individual fields, because the
# required validation will be handled by MultiValueField, not by those
# individual fields.
for f in fields:
f.required = False
self.fields = fields
def clean(self, value):
"""
Validates every value in the given list. A value is validated against
the corresponding Field in self.fields.
For example, if this MultiValueField was instantiated with
fields=(DateField(), TimeField()), clean() would call
DateField.clean(value[0]) and TimeField.clean(value[1]).
"""
clean_data = []
errors = ErrorList()
if self.required and not value:
raise ValidationError(gettext(u'This field is required.'))
elif not self.required and not value:
return self.compress([])
if not isinstance(value, (list, tuple)):
raise ValidationError(gettext(u'Enter a list of values.'))
for i, field in enumerate(self.fields):
try:
field_value = value[i]
except KeyError:
field_value = None
if self.required and field_value in EMPTY_VALUES:
raise ValidationError(gettext(u'This field is required.'))
try:
clean_data.append(field.clean(field_value))
except ValidationError, e:
# Collect all validation errors in a single list, which we'll
# raise at the end of clean(), rather than raising a single
# exception for the first error we encounter.
errors.extend(e.messages)
if errors:
raise ValidationError(errors)
return self.compress(clean_data)
def compress(self, data_list):
"""
Returns a single value for the given list of values. The values can be
assumed to be valid.
For example, if this MultiValueField was instantiated with
fields=(DateField(), TimeField()), this might return a datetime
object created by combining the date and time in data_list.
"""
raise NotImplementedError('Subclasses must implement this method.')
class SplitDateTimeField(MultiValueField):
def __init__(self, *args, **kwargs):
fields = (DateField(), TimeField())
super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
def compress(self, data_list):
if data_list:
return datetime.datetime.combine(*data_list)
return None

View File

@ -5,8 +5,8 @@ Form classes
from django.utils.datastructures import SortedDict, MultiValueDict from django.utils.datastructures import SortedDict, MultiValueDict
from django.utils.html import escape from django.utils.html import escape
from fields import Field from fields import Field
from widgets import TextInput, Textarea, HiddenInput from widgets import TextInput, Textarea, HiddenInput, MultipleHiddenInput
from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError from util import flatatt, StrAndUnicode, ErrorDict, ErrorList, ValidationError
__all__ = ('BaseForm', 'Form') __all__ = ('BaseForm', 'Form')
@ -26,12 +26,15 @@ class SortedDictFromList(SortedDict):
self.keyOrder = [d[0] for d in data] self.keyOrder = [d[0] for d in data]
dict.__init__(self, dict(data)) dict.__init__(self, dict(data))
def copy(self):
return SortedDictFromList(self.items())
class DeclarativeFieldsMetaclass(type): class DeclarativeFieldsMetaclass(type):
"Metaclass that converts Field attributes to a dictionary called 'fields'." "Metaclass that converts Field attributes to a dictionary called 'base_fields'."
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)] fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
attrs['fields'] = SortedDictFromList(fields) attrs['base_fields'] = SortedDictFromList(fields)
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
class BaseForm(StrAndUnicode): class BaseForm(StrAndUnicode):
@ -39,13 +42,19 @@ class BaseForm(StrAndUnicode):
# class is different than Form. See the comments by the Form class for more # 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* # information. Any improvements to the form API should be made to *this*
# class, not to the Form class. # class, not to the Form class.
def __init__(self, data=None, auto_id='id_%s', prefix=None): def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):
self.ignore_errors = data is None self.is_bound = data is not None
self.data = data or {} self.data = data or {}
self.auto_id = auto_id self.auto_id = auto_id
self.prefix = prefix self.prefix = prefix
self.clean_data = None # Stores the data after clean() has been called. self.initial = initial or {}
self.__errors = None # Stores the errors after clean() has been called. self.__errors = None # Stores the errors after clean() has been called.
# The base_fields class attribute is the *class-wide* definition of
# fields. Because a particular *instance* of the class might want to
# alter self.fields, we create self.fields here by copying base_fields.
# Instances should always modify self.fields; they should not modify
# self.base_fields.
self.fields = self.base_fields.copy()
def __unicode__(self): def __unicode__(self):
return self.as_table() return self.as_table()
@ -74,7 +83,7 @@ class BaseForm(StrAndUnicode):
Returns True if the form has no errors. Otherwise, False. If errors are Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False. being ignored, returns False.
""" """
return not self.ignore_errors and not bool(self.errors) return self.is_bound and not bool(self.errors)
def add_prefix(self, field_name): def add_prefix(self, field_name):
""" """
@ -85,7 +94,7 @@ class BaseForm(StrAndUnicode):
""" """
return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name 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): def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
top_errors = self.non_field_errors() # Errors that should be displayed above all fields. top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
output, hidden_fields = [], [] output, hidden_fields = [], []
@ -100,7 +109,11 @@ class BaseForm(StrAndUnicode):
if errors_on_separate_row and bf_errors: if errors_on_separate_row and bf_errors:
output.append(error_row % bf_errors) output.append(error_row % bf_errors)
label = bf.label and bf.label_tag(escape(bf.label + ':')) or '' label = bf.label and bf.label_tag(escape(bf.label + ':')) or ''
output.append(normal_row % {'errors': bf_errors, 'label': label, 'field': bf}) if field.help_text:
help_text = help_text_html % field.help_text
else:
help_text = u''
output.append(normal_row % {'errors': bf_errors, 'label': label, 'field': unicode(bf), 'help_text': help_text})
if top_errors: if top_errors:
output.insert(0, error_row % top_errors) output.insert(0, error_row % top_errors)
if hidden_fields: # Insert any hidden fields in the last row. if hidden_fields: # Insert any hidden fields in the last row.
@ -115,15 +128,15 @@ class BaseForm(StrAndUnicode):
def as_table(self): def as_table(self):
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>." "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', False) return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
def as_ul(self): def as_ul(self):
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
return self._html_output(u'<li>%(errors)s%(label)s %(field)s</li>', u'<li>%s</li>', '</li>', False) return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
def as_p(self): def as_p(self):
"Returns this form rendered as HTML <p>s." "Returns this form rendered as HTML <p>s."
return self._html_output(u'<p>%(label)s %(field)s</p>', u'<p>%s</p>', '</p>', True) return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'<p>%s</p>', '</p>', u' %s', True)
def non_field_errors(self): def non_field_errors(self):
""" """
@ -137,11 +150,11 @@ class BaseForm(StrAndUnicode):
""" """
Cleans all of self.data and populates self.__errors and self.clean_data. Cleans all of self.data and populates self.__errors and self.clean_data.
""" """
self.clean_data = {}
errors = ErrorDict() errors = ErrorDict()
if self.ignore_errors: # Stop further processing. if not self.is_bound: # Stop further processing.
self.__errors = errors self.__errors = errors
return return
self.clean_data = {}
for name, field in self.fields.items(): for name, field in self.fields.items():
# value_from_datadict() gets the data from the dictionary. # value_from_datadict() gets the data from the dictionary.
# Each widget type knows how to retrieve its own data, because some # Each widget type knows how to retrieve its own data, because some
@ -160,7 +173,7 @@ class BaseForm(StrAndUnicode):
except ValidationError, e: except ValidationError, e:
errors[NON_FIELD_ERRORS] = e.messages errors[NON_FIELD_ERRORS] = e.messages
if errors: if errors:
self.clean_data = None delattr(self, 'clean_data')
self.__errors = errors self.__errors = errors
def clean(self): def clean(self):
@ -218,8 +231,8 @@ class BoundField(StrAndUnicode):
auto_id = self.auto_id auto_id = self.auto_id
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'): if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
attrs['id'] = auto_id attrs['id'] = auto_id
if self.form.ignore_errors: if not self.form.is_bound:
data = self.field.initial data = self.form.initial.get(self.name, self.field.initial)
else: else:
data = self.data data = self.data
return widget.render(self.html_name, data, attrs=attrs) return widget.render(self.html_name, data, attrs=attrs)
@ -238,7 +251,7 @@ class BoundField(StrAndUnicode):
""" """
Returns a string of HTML for representing this as an <input type="hidden">. Returns a string of HTML for representing this as an <input type="hidden">.
""" """
return self.as_widget(HiddenInput(), attrs) return self.as_widget(self.field.hidden_widget(), attrs)
def _data(self): def _data(self):
""" """
@ -247,17 +260,20 @@ class BoundField(StrAndUnicode):
return self.field.widget.value_from_datadict(self.form.data, self.html_name) return self.field.widget.value_from_datadict(self.form.data, self.html_name)
data = property(_data) data = property(_data)
def label_tag(self, contents=None): def label_tag(self, contents=None, attrs=None):
""" """
Wraps the given contents in a <label>, if the field has an ID attribute. Wraps the given contents in a <label>, if the field has an ID attribute.
Does not HTML-escape the contents. If contents aren't given, uses the Does not HTML-escape the contents. If contents aren't given, uses the
field's HTML-escaped label. field's HTML-escaped label.
If attrs are given, they're used as HTML attributes on the <label> tag.
""" """
contents = contents or escape(self.label) contents = contents or escape(self.label)
widget = self.field.widget widget = self.field.widget
id_ = widget.attrs.get('id') or self.auto_id id_ = widget.attrs.get('id') or self.auto_id
if id_: if id_:
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents) attrs = attrs and flatatt(attrs) or ''
contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents)
return contents return contents
def _is_hidden(self): def _is_hidden(self):

View File

@ -5,9 +5,9 @@ and database field objects.
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
__all__ = ('form_for_model', 'form_for_instance', 'form_for_fields') __all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields')
def create(self, save=True): def model_save(self, commit=True):
""" """
Creates and returns model instance according to self.clean_data. Creates and returns model instance according to self.clean_data.
@ -15,61 +15,84 @@ def create(self, save=True):
""" """
if self.errors: if self.errors:
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name) 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) return save_instance(self, self._model(), commit)
if save:
obj.save()
return obj
def make_apply_changes(opts, instance): def save_instance(form, instance, commit=True):
"Returns the apply_changes() method for a form_for_instance Form." """
Saves bound Form ``form``'s clean_data into model instance ``instance``.
Assumes ``form`` has a field for every non-AutoField database field in
``instance``. If commit=True, then the changes to ``instance`` will be
saved to the database. Returns ``instance``.
"""
from django.db import models from django.db import models
def apply_changes(self, save=True): opts = instance.__class__._meta
if self.errors: if form.errors:
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name) raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
clean_data = self.clean_data clean_data = form.clean_data
for f in opts.fields + opts.many_to_many: for f in opts.fields:
if isinstance(f, models.AutoField): if isinstance(f, models.AutoField):
continue continue
setattr(instance, f.attname, clean_data[f.name]) setattr(instance, f.attname, clean_data[f.name])
if save: if commit:
instance.save() instance.save()
for f in opts.many_to_many:
setattr(instance, f.attname, clean_data[f.name])
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
# data will be lost. This happens because a many-to-many options cannot be
# set on an object until after it's saved. Maybe we should raise an
# exception in that case.
return instance return instance
return apply_changes
def form_for_model(model, form=BaseForm): def make_instance_save(instance):
"Returns the save() method for a form_for_instance Form."
def save(self, commit=True):
return save_instance(self, instance, commit)
return save
def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield()):
""" """
Returns a Form class for the given Django model class. Returns a Form class for the given Django model class.
Provide 'form' if you want to use a custom BaseForm subclass. Provide ``form`` if you want to use a custom BaseForm subclass.
Provide ``formfield_callback`` if you want to define different logic for
determining the formfield for a given database field. It's a callable that
takes a database Field instance and returns a form Field instance.
""" """
opts = model._meta opts = model._meta
field_list = [] field_list = []
for f in opts.fields + opts.many_to_many: for f in opts.fields + opts.many_to_many:
formfield = f.formfield() formfield = formfield_callback(f)
if formfield: if formfield:
field_list.append((f.name, formfield)) field_list.append((f.name, formfield))
fields = SortedDictFromList(field_list) fields = SortedDictFromList(field_list)
return type(opts.object_name + 'Form', (form,), {'fields': fields, '_model': model, 'create': create}) return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save})
def form_for_instance(instance, form=BaseForm): def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
""" """
Returns a Form class for the given Django model instance. Returns a Form class for the given Django model instance.
Provide 'form' if you want to use a custom BaseForm subclass. Provide ``form`` if you want to use a custom BaseForm subclass.
Provide ``formfield_callback`` if you want to define different logic for
determining the formfield for a given database field. It's a callable that
takes a database Field instance, plus **kwargs, and returns a form Field
instance with the given kwargs (i.e. 'initial').
""" """
model = instance.__class__ model = instance.__class__
opts = model._meta opts = model._meta
field_list = [] field_list = []
for f in opts.fields + opts.many_to_many: for f in opts.fields + opts.many_to_many:
current_value = f.value_from_object(instance) current_value = f.value_from_object(instance)
formfield = f.formfield(initial=current_value) formfield = formfield_callback(f, initial=current_value)
if formfield: if formfield:
field_list.append((f.name, formfield)) field_list.append((f.name, formfield))
fields = SortedDictFromList(field_list) fields = SortedDictFromList(field_list)
return type(opts.object_name + 'InstanceForm', (form,), return type(opts.object_name + 'InstanceForm', (form,),
{'fields': fields, '_model': model, 'apply_changes': make_apply_changes(opts, instance)}) {'base_fields': fields, '_model': model, 'save': make_instance_save(instance)})
def form_for_fields(field_list): def form_for_fields(field_list):
"Returns a Form class for the given list of Django database field instances." "Returns a Form class for the given list of Django database field instances."
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list]) fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
return type('FormForFields', (BaseForm,), {'fields': fields}) return type('FormForFields', (BaseForm,), {'base_fields': fields})

View File

@ -1,4 +1,9 @@
from django.conf import settings from django.conf import settings
from django.utils.html import escape
# Converts a dictionary to a single string with key="value", XML-style with
# a leading space. Assumes keys do not need to be XML-escaped.
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
def smart_unicode(s): def smart_unicode(s):
if not isinstance(s, basestring): if not isinstance(s, basestring):

View File

@ -3,14 +3,16 @@ HTML Widget classes
""" """
__all__ = ( __all__ = (
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput', 'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',
'Textarea', 'CheckboxInput', 'FileInput', 'Textarea', 'CheckboxInput',
'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple', 'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
'MultiWidget', 'SplitDateTimeWidget',
) )
from util import StrAndUnicode, smart_unicode from util import flatatt, StrAndUnicode, smart_unicode
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from django.utils.html import escape from django.utils.html import escape
from django.utils.translation import gettext
from itertools import chain from itertools import chain
try: try:
@ -18,10 +20,6 @@ try:
except NameError: except NameError:
from sets import Set as set # Python 2.3 fallback from sets import Set as set # Python 2.3 fallback
# Converts a dictionary to a single string with key="value", XML-style with
# a leading space. Assumes keys do not need to be XML-escaped.
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
class Widget(object): class Widget(object):
is_hidden = False # Determines whether this corresponds to an <input type="hidden">. is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
@ -87,6 +85,26 @@ class HiddenInput(Input):
input_type = 'hidden' input_type = 'hidden'
is_hidden = True is_hidden = True
class MultipleHiddenInput(HiddenInput):
"""
A widget that handles <input type="hidden"> for fields that have a list
of values.
"""
def __init__(self, attrs=None, choices=()):
# choices can be any iterable
self.attrs = attrs or {}
self.choices = choices
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
return u'\n'.join([(u'<input%s />' % flatatt(dict(value=smart_unicode(v), **final_attrs))) for v in value])
def value_from_datadict(self, data, name):
if isinstance(data, MultiValueDict):
return data.getlist(name)
return data.get(name, None)
class FileInput(Input): class FileInput(Input):
input_type = 'file' input_type = 'file'
@ -118,9 +136,11 @@ class CheckboxInput(Widget):
class Select(Widget): class Select(Widget):
def __init__(self, attrs=None, choices=()): def __init__(self, attrs=None, choices=()):
# choices can be any iterable
self.attrs = attrs or {} self.attrs = attrs or {}
self.choices = choices # choices can be any iterable, but we may need to render this widget
# multiple times. Thus, collapse it into a list so it can be consumed
# more than once.
self.choices = list(choices)
def render(self, name, value, attrs=None, choices=()): def render(self, name, value, attrs=None, choices=()):
if value is None: value = '' if value is None: value = ''
@ -134,6 +154,25 @@ class Select(Widget):
output.append(u'</select>') output.append(u'</select>')
return u'\n'.join(output) return u'\n'.join(output)
class NullBooleanSelect(Select):
"""
A Select Widget intended to be used with NullBooleanField.
"""
def __init__(self, attrs=None):
choices = ((u'1', gettext('Unknown')), (u'2', gettext('Yes')), (u'3', gettext('No')))
super(NullBooleanSelect, self).__init__(attrs, choices)
def render(self, name, value, attrs=None, choices=()):
try:
value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value]
except KeyError:
value = u'1'
return super(NullBooleanSelect, self).render(name, value, attrs, choices)
def value_from_datadict(self, data, name):
value = data.get(name, None)
return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
class SelectMultiple(Widget): class SelectMultiple(Widget):
def __init__(self, attrs=None, choices=()): def __init__(self, attrs=None, choices=()):
# choices can be any iterable # choices can be any iterable
@ -162,14 +201,15 @@ class RadioInput(StrAndUnicode):
def __init__(self, name, value, attrs, choice, index): def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value self.name, self.value = name, value
self.attrs = attrs self.attrs = attrs
self.choice_value, self.choice_label = choice self.choice_value = smart_unicode(choice[0])
self.choice_label = smart_unicode(choice[1])
self.index = index self.index = index
def __unicode__(self): def __unicode__(self):
return u'<label>%s %s</label>' % (self.tag(), self.choice_label) return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
def is_checked(self): def is_checked(self):
return self.value == smart_unicode(self.choice_value) return self.value == self.choice_value
def tag(self): def tag(self):
if self.attrs.has_key('id'): if self.attrs.has_key('id'):
@ -218,11 +258,16 @@ class RadioSelect(Select):
class CheckboxSelectMultiple(SelectMultiple): class CheckboxSelectMultiple(SelectMultiple):
def render(self, name, value, attrs=None, choices=()): def render(self, name, value, attrs=None, choices=()):
if value is None: value = [] if value is None: value = []
has_id = attrs and attrs.has_key('id')
final_attrs = self.build_attrs(attrs, name=name) final_attrs = self.build_attrs(attrs, name=name)
output = [u'<ul>'] output = [u'<ul>']
str_values = set([smart_unicode(v) for v in value]) # Normalize to strings. str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
# If an ID attribute was given, add a numeric index as a suffix,
# so that the checkboxes don't all have the same ID attribute.
if has_id:
final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values) cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
for option_value, option_label in chain(self.choices, choices):
option_value = smart_unicode(option_value) option_value = smart_unicode(option_value)
rendered_cb = cb.render(name, option_value) rendered_cb = cb.render(name, option_value)
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label)))) output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label))))
@ -235,3 +280,66 @@ class CheckboxSelectMultiple(SelectMultiple):
id_ += '_0' id_ += '_0'
return id_ return id_
id_for_label = classmethod(id_for_label) id_for_label = classmethod(id_for_label)
class MultiWidget(Widget):
"""
A widget that is composed of multiple widgets.
Its render() method takes a "decompressed" list of values, not a single
value. Each value in this list is rendered in the corresponding widget --
the first value is rendered in the first widget, the second value is
rendered in the second widget, etc.
Subclasses should implement decompress(), which specifies how a single
value should be converted to a list of values. Subclasses should not
have to implement clean().
Subclasses may implement format_output(), which takes the list of rendered
widgets and returns HTML that formats them any way you'd like.
You'll probably want to use this with MultiValueField.
"""
def __init__(self, widgets, attrs=None):
self.widgets = [isinstance(w, type) and w() or w for w in widgets]
super(MultiWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
# value is a list of values, each corresponding to a widget
# in self.widgets.
if not isinstance(value, list):
value = self.decompress(value)
output = []
for i, widget in enumerate(self.widgets):
try:
widget_value = value[i]
except KeyError:
widget_value = None
output.append(widget.render(name + '_%s' % i, widget_value, attrs))
return self.format_output(output)
def value_from_datadict(self, data, name):
return [data.get(name + '_%s' % i) for i in range(len(self.widgets))]
def format_output(self, rendered_widgets):
return u''.join(rendered_widgets)
def decompress(self, value):
"""
Returns a list of decompressed values for the given compressed value.
The given value can be assumed to be valid, but not necessarily
non-empty.
"""
raise NotImplementedError('Subclasses must implement this method.')
class SplitDateTimeWidget(MultiWidget):
"""
A Widget that splits datetime input into two <input type="text"> boxes.
"""
def __init__(self, attrs=None):
widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs))
super(SplitDateTimeWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return [value.date(), value.time()]
return [None, None]

View File

@ -569,7 +569,7 @@ class NullBooleanField(SelectField):
"This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None" "This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None"
def __init__(self, field_name, is_required=False, validator_list=None): def __init__(self, field_name, is_required=False, validator_list=None):
if validator_list is None: validator_list = [] if validator_list is None: validator_list = []
SelectField.__init__(self, field_name, choices=[('1', 'Unknown'), ('2', 'Yes'), ('3', 'No')], SelectField.__init__(self, field_name, choices=[('1', _('Unknown')), ('2', _('Yes')), ('3', _('No'))],
is_required=is_required, validator_list=validator_list) is_required=is_required, validator_list=validator_list)
def render(self, data): def render(self, data):
@ -958,7 +958,9 @@ class USStateField(TextField):
raise validators.CriticalValidationError, e.messages raise validators.CriticalValidationError, e.messages
def html2python(data): def html2python(data):
if data:
return data.upper() # Should always be stored in upper case return data.upper() # Should always be stored in upper case
return data
html2python = staticmethod(html2python) html2python = staticmethod(html2python)
class CommaSeparatedIntegerField(TextField): class CommaSeparatedIntegerField(TextField):

View File

@ -70,7 +70,7 @@ class SortedDict(dict):
return self.keyOrder[:] return self.keyOrder[:]
def values(self): def values(self):
return [dict.__getitem__(self,k) for k in self.keyOrder] return [dict.__getitem__(self, k) for k in self.keyOrder]
def update(self, dict): def update(self, dict):
for k, v in dict.items(): for k, v in dict.items():
@ -81,6 +81,10 @@ class SortedDict(dict):
self.keyOrder.append(key) self.keyOrder.append(key)
return dict.setdefault(self, key, default) return dict.setdefault(self, key, default)
def value_for_index(self, index):
"Returns the value of the item at the given zero-based index."
return self[self.keyOrder[index]]
class MultiValueDictKeyError(KeyError): class MultiValueDictKeyError(KeyError):
pass pass

View File

@ -1,4 +1,4 @@
simplejson 1.3 simplejson 1.5
Copyright (c) 2006 Bob Ippolito Copyright (c) 2006 Bob Ippolito
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of

View File

@ -27,6 +27,21 @@ Encoding basic Python object hierarchies::
>>> io.getvalue() >>> io.getvalue()
'["streaming API"]' '["streaming API"]'
Compact encoding::
>>> import simplejson
>>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
'[1,2,3,{"4":5,"6":7}]'
Pretty printing::
>>> import simplejson
>>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
{
"4": 5,
"6": 7
}
Decoding JSON:: Decoding JSON::
>>> import simplejson >>> import simplejson
@ -68,10 +83,10 @@ Extending JSONEncoder::
['[', '2.0', ', ', '1.0', ']'] ['[', '2.0', ', ', '1.0', ']']
Note that the JSON produced by this module is a subset of YAML, Note that the JSON produced by this module's default settings
so it may be used as a serializer for that as well. is a subset of YAML, so it may be used as a serializer for that as well.
""" """
__version__ = '1.3' __version__ = '1.5'
__all__ = [ __all__ = [
'dump', 'dumps', 'load', 'loads', 'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONEncoder', 'JSONDecoder', 'JSONEncoder',
@ -81,7 +96,7 @@ from django.utils.simplejson.decoder import JSONDecoder
from django.utils.simplejson.encoder import JSONEncoder from django.utils.simplejson.encoder import JSONEncoder
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, **kw): allow_nan=True, cls=None, indent=None, **kw):
""" """
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
``.write()``-supporting file-like object). ``.write()``-supporting file-like object).
@ -105,6 +120,10 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
in strict compliance of the JSON specification, instead of using the in strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
If ``indent`` is a non-negative integer, then JSON array elements and object
members will be pretty-printed with that indent level. An indent level
of 0 will only insert newlines. ``None`` is the most compact representation.
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with ``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg. the ``cls`` kwarg.
@ -112,7 +131,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
if cls is None: if cls is None:
cls = JSONEncoder cls = JSONEncoder
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, check_circular=check_circular, allow_nan=allow_nan, indent=indent,
**kw).iterencode(obj) **kw).iterencode(obj)
# could accelerate with writelines in some versions of Python, at # could accelerate with writelines in some versions of Python, at
# a debuggability cost # a debuggability cost
@ -120,7 +139,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
fp.write(chunk) fp.write(chunk)
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, **kw): allow_nan=True, cls=None, indent=None, separators=None, **kw):
""" """
Serialize ``obj`` to a JSON formatted ``str``. Serialize ``obj`` to a JSON formatted ``str``.
@ -141,14 +160,26 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
strict compliance of the JSON specification, instead of using the strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
If ``indent`` is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level. An indent
level of 0 will only insert newlines. ``None`` is the most compact
representation.
If ``separators`` is an ``(item_separator, dict_separator)`` tuple
then it will be used instead of the default ``(', ', ': ')`` separators.
``(',', ':')`` is the most compact JSON representation.
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with ``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg. the ``cls`` kwarg.
""" """
if cls is None: if cls is None:
cls = JSONEncoder cls = JSONEncoder
return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, return cls(
check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj) skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators,
**kw).encode(obj)
def load(fp, encoding=None, cls=None, object_hook=None, **kw): def load(fp, encoding=None, cls=None, object_hook=None, **kw):
""" """

View File

@ -127,6 +127,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
raise ValueError(errmsg("Expecting property name", s, end)) raise ValueError(errmsg("Expecting property name", s, end))
end += 1 end += 1
encoding = getattr(context, 'encoding', None) encoding = getattr(context, 'encoding', None)
iterscan = JSONScanner.iterscan
while True: while True:
key, end = scanstring(s, end, encoding) key, end = scanstring(s, end, encoding)
end = _w(s, end).end() end = _w(s, end).end()
@ -134,7 +135,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
raise ValueError(errmsg("Expecting : delimiter", s, end)) raise ValueError(errmsg("Expecting : delimiter", s, end))
end = _w(s, end + 1).end() end = _w(s, end + 1).end()
try: try:
value, end = JSONScanner.iterscan(s, idx=end).next() value, end = iterscan(s, idx=end, context=context).next()
except StopIteration: except StopIteration:
raise ValueError(errmsg("Expecting object", s, end)) raise ValueError(errmsg("Expecting object", s, end))
pairs[key] = value pairs[key] = value
@ -164,9 +165,10 @@ def JSONArray(match, context, _w=WHITESPACE.match):
nextchar = s[end:end + 1] nextchar = s[end:end + 1]
if nextchar == ']': if nextchar == ']':
return values, end + 1 return values, end + 1
iterscan = JSONScanner.iterscan
while True: while True:
try: try:
value, end = JSONScanner.iterscan(s, idx=end).next() value, end = iterscan(s, idx=end, context=context).next()
except StopIteration: except StopIteration:
raise ValueError(errmsg("Expecting object", s, end)) raise ValueError(errmsg("Expecting object", s, end))
values.append(value) values.append(value)

View File

@ -3,11 +3,11 @@ Implementation of JSONEncoder
""" """
import re import re
# this should match any kind of infinity
INFCHARS = re.compile(r'[infINF]')
ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]') ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
ESCAPE_DCT = { ESCAPE_DCT = {
# escape all forward slashes to prevent </script> attack
'/': '\\/',
'\\': '\\\\', '\\': '\\\\',
'"': '\\"', '"': '\\"',
'\b': '\\b', '\b': '\\b',
@ -16,31 +16,31 @@ ESCAPE_DCT = {
'\r': '\\r', '\r': '\\r',
'\t': '\\t', '\t': '\\t',
} }
for i in range(20): for i in range(0x20):
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,)) ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
# assume this produces an infinity on all machines (probably not guaranteed)
INFINITY = float('1e66666')
def floatstr(o, allow_nan=True): def floatstr(o, allow_nan=True):
s = str(o) # Check for specials. Note that this type of test is processor- and/or
# If the first non-sign is a digit then it's not a special value # platform-specific, so do tests which don't depend on the internals.
if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
return s if o != o:
elif not allow_nan: text = 'NaN'
elif o == INFINITY:
text = 'Infinity'
elif o == -INFINITY:
text = '-Infinity'
else:
return str(o)
if not allow_nan:
raise ValueError("Out of range float values are not JSON compliant: %r" raise ValueError("Out of range float values are not JSON compliant: %r"
% (o,)) % (o,))
# These are the string representations on the platforms I've tried
if s == 'nan': return text
return 'NaN'
if s == 'inf':
return 'Infinity'
if s == '-inf':
return '-Infinity'
# NaN should either be inequal to itself, or equal to everything
if o != o or o == 0.0:
return 'NaN'
# Last ditch effort, assume inf
if o < 0:
return '-Infinity'
return 'Infinity'
def encode_basestring(s): def encode_basestring(s):
""" """
@ -90,8 +90,11 @@ class JSONEncoder(object):
implementation (to raise ``TypeError``). implementation (to raise ``TypeError``).
""" """
__all__ = ['__init__', 'default', 'encode', 'iterencode'] __all__ = ['__init__', 'default', 'encode', 'iterencode']
item_separator = ', '
key_separator = ': '
def __init__(self, skipkeys=False, ensure_ascii=True, def __init__(self, skipkeys=False, ensure_ascii=True,
check_circular=True, allow_nan=True, sort_keys=False): check_circular=True, allow_nan=True, sort_keys=False,
indent=None, separators=None):
""" """
Constructor for JSONEncoder, with sensible defaults. Constructor for JSONEncoder, with sensible defaults.
@ -116,6 +119,15 @@ class JSONEncoder(object):
If sort_keys is True, then the output of dictionaries will be If sort_keys is True, then the output of dictionaries will be
sorted by key; this is useful for regression tests to ensure sorted by key; this is useful for regression tests to ensure
that JSON serializations can be compared on a day-to-day basis. that JSON serializations can be compared on a day-to-day basis.
If indent is a non-negative integer, then JSON array
elements and object members will be pretty-printed with that
indent level. An indent level of 0 will only insert newlines.
None is the most compact representation.
If specified, separators should be a (item_separator, key_separator)
tuple. The default is (', ', ': '). To get the most compact JSON
representation you should specify (',', ':') to eliminate whitespace.
""" """
self.skipkeys = skipkeys self.skipkeys = skipkeys
@ -123,6 +135,13 @@ class JSONEncoder(object):
self.check_circular = check_circular self.check_circular = check_circular
self.allow_nan = allow_nan self.allow_nan = allow_nan
self.sort_keys = sort_keys self.sort_keys = sort_keys
self.indent = indent
self.current_indent_level = 0
if separators is not None:
self.item_separator, self.key_separator = separators
def _newline_indent(self):
return '\n' + (' ' * (self.indent * self.current_indent_level))
def _iterencode_list(self, lst, markers=None): def _iterencode_list(self, lst, markers=None):
if not lst: if not lst:
@ -134,14 +153,25 @@ class JSONEncoder(object):
raise ValueError("Circular reference detected") raise ValueError("Circular reference detected")
markers[markerid] = lst markers[markerid] = lst
yield '[' yield '['
if self.indent is not None:
self.current_indent_level += 1
newline_indent = self._newline_indent()
separator = self.item_separator + newline_indent
yield newline_indent
else:
newline_indent = None
separator = self.item_separator
first = True first = True
for value in lst: for value in lst:
if first: if first:
first = False first = False
else: else:
yield ', ' yield separator
for chunk in self._iterencode(value, markers): for chunk in self._iterencode(value, markers):
yield chunk yield chunk
if newline_indent is not None:
self.current_indent_level -= 1
yield self._newline_indent()
yield ']' yield ']'
if markers is not None: if markers is not None:
del markers[markerid] del markers[markerid]
@ -156,6 +186,15 @@ class JSONEncoder(object):
raise ValueError("Circular reference detected") raise ValueError("Circular reference detected")
markers[markerid] = dct markers[markerid] = dct
yield '{' yield '{'
key_separator = self.key_separator
if self.indent is not None:
self.current_indent_level += 1
newline_indent = self._newline_indent()
item_separator = self.item_separator + newline_indent
yield newline_indent
else:
newline_indent = None
item_separator = self.item_separator
first = True first = True
if self.ensure_ascii: if self.ensure_ascii:
encoder = encode_basestring_ascii encoder = encode_basestring_ascii
@ -165,7 +204,7 @@ class JSONEncoder(object):
if self.sort_keys: if self.sort_keys:
keys = dct.keys() keys = dct.keys()
keys.sort() keys.sort()
items = [(k,dct[k]) for k in keys] items = [(k, dct[k]) for k in keys]
else: else:
items = dct.iteritems() items = dct.iteritems()
for key, value in items: for key, value in items:
@ -190,11 +229,14 @@ class JSONEncoder(object):
if first: if first:
first = False first = False
else: else:
yield ', ' yield item_separator
yield encoder(key) yield encoder(key)
yield ': ' yield key_separator
for chunk in self._iterencode(value, markers): for chunk in self._iterencode(value, markers):
yield chunk yield chunk
if newline_indent is not None:
self.current_indent_level -= 1
yield self._newline_indent()
yield '}' yield '}'
if markers is not None: if markers is not None:
del markers[markerid] del markers[markerid]

View File

@ -0,0 +1,40 @@
from django.utils import simplejson
import cgi
class JSONFilter(object):
def __init__(self, app, mime_type='text/x-json'):
self.app = app
self.mime_type = mime_type
def __call__(self, environ, start_response):
# Read JSON POST input to jsonfilter.json if matching mime type
response = {'status': '200 OK', 'headers': []}
def json_start_response(status, headers):
response['status'] = status
response['headers'].extend(headers)
environ['jsonfilter.mime_type'] = self.mime_type
if environ.get('REQUEST_METHOD', '') == 'POST':
if environ.get('CONTENT_TYPE', '') == self.mime_type:
args = [_ for _ in [environ.get('CONTENT_LENGTH')] if _]
data = environ['wsgi.input'].read(*map(int, args))
environ['jsonfilter.json'] = simplejson.loads(data)
res = simplejson.dumps(self.app(environ, json_start_response))
jsonp = cgi.parse_qs(environ.get('QUERY_STRING', '')).get('jsonp')
if jsonp:
content_type = 'text/javascript'
res = ''.join(jsonp + ['(', res, ')'])
elif 'Opera' in environ.get('HTTP_USER_AGENT', ''):
# Opera has bunk XMLHttpRequest support for most mime types
content_type = 'text/plain'
else:
content_type = self.mime_type
headers = [
('Content-type', content_type),
('Content-length', len(res)),
]
headers.extend(response['headers'])
start_response(response['status'], headers)
return [res]
def factory(app, global_conf, **kw):
return JSONFilter(app, **kw)

View File

@ -3,11 +3,12 @@ Iterator based sre token scanner
""" """
import sre_parse, sre_compile, sre_constants import sre_parse, sre_compile, sre_constants
from sre_constants import BRANCH, SUBPATTERN from sre_constants import BRANCH, SUBPATTERN
from re import VERBOSE, MULTILINE, DOTALL
import re import re
__all__ = ['Scanner', 'pattern'] __all__ = ['Scanner', 'pattern']
FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL) FLAGS = (VERBOSE | MULTILINE | DOTALL)
class Scanner(object): class Scanner(object):
def __init__(self, lexicon, flags=FLAGS): def __init__(self, lexicon, flags=FLAGS):
self.actions = [None] self.actions = [None]

View File

@ -17,7 +17,7 @@ admin
The automatic Django administrative interface. For more information, see The automatic Django administrative interface. For more information, see
`Tutorial 2`_. `Tutorial 2`_.
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/ .. _Tutorial 2: ../tutorial2/
auth auth
==== ====
@ -26,7 +26,7 @@ Django's authentication framework.
See the `authentication documentation`_. See the `authentication documentation`_.
.. _authentication documentation: http://www.djangoproject.com/documentation/authentication/ .. _authentication documentation: ../authentication/
comments comments
======== ========
@ -46,7 +46,7 @@ A middleware for preventing Cross Site Request Forgeries
See the `csrf documentation`_. See the `csrf documentation`_.
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/ .. _csrf documentation: ../csrf/
formtools formtools
========= =========
@ -137,7 +137,7 @@ A framework for managing simple "flat" HTML content in a database.
See the `flatpages documentation`_. See the `flatpages documentation`_.
.. _flatpages documentation: http://www.djangoproject.com/documentation/flatpages/ .. _flatpages documentation: ../flatpages/
markup markup
====== ======
@ -157,7 +157,7 @@ A framework for managing redirects.
See the `redirects documentation`_. See the `redirects documentation`_.
.. _redirects documentation: http://www.djangoproject.com/documentation/redirects/ .. _redirects documentation: ../redirects/
sites sites
===== =====
@ -168,7 +168,7 @@ one or more sites.
See the `sites documentation`_. See the `sites documentation`_.
.. _sites documentation: http://www.djangoproject.com/documentation/sites/ .. _sites documentation: ../sites/
sitemaps sitemaps
======== ========
@ -177,7 +177,7 @@ A framework for generating Google sitemap XML files.
See the `sitemaps documentation`_. See the `sitemaps documentation`_.
.. _sitemaps documentation: http://www.djangoproject.com/documentation/sitemaps/ .. _sitemaps documentation: ../sitemaps/
syndication syndication
=========== ===========
@ -186,7 +186,7 @@ A framework for generating syndication feeds, in RSS and Atom, quite easily.
See the `syndication documentation`_. See the `syndication documentation`_.
.. _syndication documentation: http://www.djangoproject.com/documentation/syndication/ .. _syndication documentation: ../syndication/
Other add-ons Other add-ons
============= =============

View File

@ -65,7 +65,7 @@ are equivalent::
SetEnv DJANGO_SETTINGS_MODULE mysite.settings SetEnv DJANGO_SETTINGS_MODULE mysite.settings
PythonOption DJANGO_SETTINGS_MODULE mysite.settings PythonOption DJANGO_SETTINGS_MODULE mysite.settings
.. _authentication system: http://www.djangoproject.com/documentation/authentication/ .. _authentication system: ../authentication/
.. _Subversion: http://subversion.tigris.org/ .. _Subversion: http://subversion.tigris.org/
.. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html .. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html
.. _custom permissions: http://www.djangoproject.com/documentation/authentication/#custom-permissions .. _custom permissions: ../authentication/#custom-permissions

View File

@ -98,26 +98,26 @@ change:
rewrite before Django 1.0. Even if the change isn't quite that drastic, rewrite before Django 1.0. Even if the change isn't quite that drastic,
there will at least be moderate changes. there will at least be moderate changes.
.. _caching: http://www.djangoproject.com/documentation/cache/ .. _caching: ../cache/
.. _custom template tags and libraries: http://www.djangoproject.com/documentation/templates_python/ .. _custom template tags and libraries: ../templates_python/
.. _database lookup: http://www.djangoproject.com/documentation/db_api/ .. _database lookup: ../db_api/
.. _django-admin utility: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin utility: ../django_admin/
.. _fastcgi integration: http://www.djangoproject.com/documentation/fastcgi/ .. _fastcgi integration: ../fastcgi/
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/ .. _flatpages: ../flatpages/
.. _generic views: http://www.djangoproject.com/documentation/generic_views/ .. _generic views: ../generic_views/
.. _internationalization: http://www.djangoproject.com/documentation/i18n/ .. _internationalization: ../i18n/
.. _legacy database integration: http://www.djangoproject.com/documentation/legacy_databases/ .. _legacy database integration: ../legacy_databases/
.. _model definition: http://www.djangoproject.com/documentation/model_api/ .. _model definition: ../model_api/
.. _mod_python integration: http://www.djangoproject.com/documentation/modpython/ .. _mod_python integration: ../modpython/
.. _redirects: http://www.djangoproject.com/documentation/redirects/ .. _redirects: ../redirects/
.. _request/response objects: http://www.djangoproject.com/documentation/request_response/ .. _request/response objects: ../request_response/
.. _sending email: http://www.djangoproject.com/documentation/email/ .. _sending email: ../email/
.. _sessions: http://www.djangoproject.com/documentation/sessions/ .. _sessions: ../sessions/
.. _settings: http://www.djangoproject.com/documentation/settings/ .. _settings: ../settings/
.. _syndication: http://www.djangoproject.com/documentation/syndication/ .. _syndication: ../syndication/
.. _template language: http://www.djangoproject.com/documentation/templates/ .. _template language: ../templates/
.. _transactions: http://www.djangoproject.com/documentation/transactions/ .. _transactions: ../transactions/
.. _url dispatch: http://www.djangoproject.com/documentation/url_dispatch/ .. _url dispatch: ../url_dispatch/
.. _forms and validation: http://www.djangoproject.com/documentation/forms/ .. _forms and validation: ../forms/
.. _serialization: http://www.djangoproject.com/documentation/serialization/ .. _serialization: ../serialization/
.. _authentication: http://www.djangoproject.com/documentation/authentication/ .. _authentication: ../authentication/

View File

@ -144,8 +144,8 @@ custom methods:
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
doesn't allow profiles. doesn't allow profiles.
.. _Django model: http://www.djangoproject.com/documentation/model_api/ .. _Django model: ../model_api/
.. _DEFAULT_FROM_EMAIL: http://www.djangoproject.com/documentation/settings/#default-from-email .. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
Manager functions Manager functions
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
@ -271,8 +271,8 @@ previous section). You can tell them apart with ``is_authenticated()``, like so:
else: else:
# Do something for anonymous users. # Do something for anonymous users.
.. _request objects: http://www.djangoproject.com/documentation/request_response/#httprequest-objects .. _request objects: ../request_response/#httprequest-objects
.. _session documentation: http://www.djangoproject.com/documentation/sessions/ .. _session documentation: ../sessions/
How to log a user in How to log a user in
-------------------- --------------------
@ -441,8 +441,8 @@ block::
{% endblock %} {% endblock %}
.. _forms documentation: http://www.djangoproject.com/documentation/forms/ .. _forms documentation: ../forms/
.. _site framework docs: http://www.djangoproject.com/documentation/sites/ .. _site framework docs: ../sites/
Limiting access to logged-in users that pass a test Limiting access to logged-in users that pass a test
--------------------------------------------------- ---------------------------------------------------
@ -544,7 +544,7 @@ For example::
def limited_object_detail(*args, **kwargs): def limited_object_detail(*args, **kwargs):
return object_detail(*args, **kwargs) return object_detail(*args, **kwargs)
.. _generic view: http://www.djangoproject.com/documentation/generic_views/ .. _generic view: ../generic_views/
Permissions Permissions
=========== ===========
@ -606,7 +606,7 @@ This example model creates three custom permissions::
The only thing this does is create those extra permissions when you run The only thing this does is create those extra permissions when you run
``syncdb``. ``syncdb``.
.. _model Meta attribute: http://www.djangoproject.com/documentation/model_api/#meta-options .. _model Meta attribute: ../model_api/#meta-options
API reference API reference
------------- -------------
@ -645,7 +645,7 @@ The currently logged-in user and his/her permissions are made available in the
setting contains ``"django.core.context_processors.auth"``, which is default. setting contains ``"django.core.context_processors.auth"``, which is default.
For more, see the `RequestContext docs`_. For more, see the `RequestContext docs`_.
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-requestcontext .. _RequestContext docs: ../templates_python/#subclassing-context-requestcontext
Users Users
----- -----
@ -691,7 +691,7 @@ Thus, you can check permissions in template ``{% if %}`` statements::
<p>You don't have permission to do anything in the foo app.</p> <p>You don't have permission to do anything in the foo app.</p>
{% endif %} {% endif %}
.. _template context: http://www.djangoproject.com/documentation/templates_python/ .. _template context: ../templates_python/
Groups Groups
====== ======
@ -756,7 +756,7 @@ scenes, so any messages will be deleted even if you don't display them.
Finally, note that this messages framework only works with users in the user Finally, note that this messages framework only works with users in the user
database. To send messages to anonymous users, use the `session framework`_. database. To send messages to anonymous users, use the `session framework`_.
.. _session framework: http://www.djangoproject.com/documentation/sessions/ .. _session framework: ../sessions/
Other authentication sources Other authentication sources
============================ ============================

View File

@ -250,7 +250,7 @@ Additionally, ``CacheMiddleware`` automatically sets a few headers in each
See the `middleware documentation`_ for more on middleware. See the `middleware documentation`_ for more on middleware.
.. _`middleware documentation`: http://www.djangoproject.com/documentation/middleware/ .. _`middleware documentation`: ../middleware/
The per-view cache The per-view cache
================== ==================

View File

@ -122,9 +122,9 @@ Patch style
* Name the patch file with a ``.diff`` extension; this will let the ticket * Name the patch file with a ``.diff`` extension; this will let the ticket
tracker apply correct syntax highlighting, which is quite helpful. tracker apply correct syntax highlighting, which is quite helpful.
* Put the prefix "[patch] " before the title of your ticket. This will make * Check the "Has patch" box on the ticket details. This will make it
it obvious that the ticket includes a patch, and it will add the ticket obvious that the ticket includes a patch, and it will add the ticket to
to the `list of tickets with patches`_. the `list of tickets with patches`_.
* The code required to fix a problem or add a feature is an essential part * 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 of a patch, but it is not the only part. A good patch should also include
@ -151,24 +151,70 @@ Unfortunately, not all bug reports in the `ticket tracker`_ provide all
the `required details`_. A number of tickets have patches, but those patches the `required details`_. A number of tickets have patches, but those patches
don't meet all the requirements of a `good patch`_. 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. One way to help out is to *triage* bugs that have been reported by other
Pick an open ticket that is missing some details, and try to replicate the users. A couple of dedicated volunteers work on this regularly, but more help
problem. Fill in the missing pieces of the report. If the ticket doesn't have is always appreciated.
a patch, create one.
Once you've completed all the missing details on the ticket and you have a Most of the workflow is based around the concept of a ticket's "triage stage".
patch with all the required features, e-mail `django-developers`_. Indicate This stage describes where in its lifetime a given ticket is at any time.
that you have triaged a ticket, and recommend a course of action for dealing Along with a handful of flags, this field easily tells us what and who each
with that ticket. ticket is waiting on.
At first, this may require you to be persistent. If you find that your triaged Since a picture is worth a thousand words, let's start there:
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 .. image:: http://media.djangoproject.com/img/doc/djangotickets.png
quality triage work, you should find that it is easier to get the developers' :height: 451
attention. :width: 590
:alt: Django's ticket workflow
We've got two roles here:
* Core developers: people with commit access who make the decisions and
write the bulk of the code.
* Ticket triagers: community members who keep track of tickets, making
sure the tickets are always categorized correctly.
Second, note the four triage stages:
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
examine the ticket and move it along.
2. "Design decision needed" means "this concept requires a design
decision," which should be discussed either in the ticket comments or on
django-developers.
3. Once a ticket is ruled to be approved for fixing, it's moved into the
"Accepted" stage. This stage is where all the real work gets done.
4. If a ticket has an associated patch (see below), a triager will review the
patch. If the patch is complete, it'll be marked as "ready for checkin" so
that a core developer knows to review and check in the patches.
The second part of this workflow involves a set of flags the describe what the
ticket has or needs in order to be "ready for checkin":
"Has patch"
The means the ticket has an associated patch_. These will be
reviewed to see if the patch is "good".
"Needs documentation"
This flag is used for tickets with patches that need associated
documentation. Complete documentation of features is a prerequisite
before we can check a fix into the codebase.
"Needs tests"
This flags the patch as needing associated unit tests. Again, this is a
required part of a valid patch.
"Patch needs improvement"
This flag means that although the ticket *has* a patch, it's not quite
ready for checkin. This could mean the patch no longer applies
cleanly, or that the code doesn't live up to our standards.
.. _required details: `Reporting bugs`_ .. _required details: `Reporting bugs`_
.. _good patch: `Patch style`_ .. _good patch: `Patch style`_
.. _patch: `Submitting patches`_
Submitting and maintaining translations Submitting and maintaining translations
======================================= =======================================
@ -186,7 +232,7 @@ translated, here's what to do:
`i18n documentation`_. `i18n documentation`_.
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/ .. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/ .. _i18n documentation: ../i18n/
Coding style Coding style
============ ============
@ -311,7 +357,7 @@ The Django tests all use the testing infrastructure that ships with Django for
testing applications. See `Testing Django applications`_ for an explanation of testing applications. See `Testing Django applications`_ for an explanation of
how to write new tests. how to write new tests.
.. _Testing Django applications: http://www.djangoproject.com/documentation/testing/ .. _Testing Django applications: ../testing/
Running the unit tests Running the unit tests
---------------------- ----------------------
@ -548,8 +594,7 @@ requests for commit access are potential flame-war starters, and will be ignored
.. _search the tracker: http://code.djangoproject.com/search .. _search the tracker: http://code.djangoproject.com/search
.. _django-users: http://groups.google.com/group/django-users .. _django-users: http://groups.google.com/group/django-users
.. _`#django`: irc://irc.freenode.net/django .. _`#django`: irc://irc.freenode.net/django
.. _list of tickets with patches: http://code.djangoproject.com/report/12 .. _list of tickets with patches: http://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority
.. _PEP 8: http://www.python.org/peps/pep-0008.html .. _PEP 8: http://www.python.org/peps/pep-0008.html
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
.. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n .. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n
.. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases .. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases

View File

@ -526,6 +526,21 @@ Examples::
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day') >>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.datetime(2005, 3, 20)] [datetime.datetime(2005, 3, 20)]
``none()``
~~~~~~~~~~
**New in Django development version**
Returns an ``EmptyQuerySet`` -- a ``QuerySet`` that always evaluates to
an empty list. This can be used in cases where you know that you should
return an empty result set and your caller is expecting a ``QuerySet``
object (instead of returning an empty list, for example.)
Examples::
>>> Entry.objects.none()
[]
``select_related()`` ``select_related()``
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~

View File

@ -186,7 +186,7 @@ code.
This is the philosophy behind `template inheritance`_. This is the philosophy behind `template inheritance`_.
.. _template inheritance: http://www.djangoproject.com/documentation/templates/#template-inheritance .. _template inheritance: ../templates/#template-inheritance
Be decoupled from HTML Be decoupled from HTML
---------------------- ----------------------

View File

@ -53,7 +53,7 @@ Prints the admin-index template snippet for the given appnames.
Use admin-index template snippets if you want to customize the look and feel of Use admin-index template snippets if you want to customize the look and feel of
your admin's index page. See `Tutorial 2`_ for more information. your admin's index page. See `Tutorial 2`_ for more information.
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/ .. _Tutorial 2: ../tutorial2/
createcachetable [tablename] createcachetable [tablename]
---------------------------- ----------------------------
@ -61,7 +61,7 @@ createcachetable [tablename]
Creates a cache table named ``tablename`` for use with the database cache Creates a cache table named ``tablename`` for use with the database cache
backend. See the `cache documentation`_ for more information. backend. See the `cache documentation`_ for more information.
.. _cache documentation: http://www.djangoproject.com/documentation/cache/ .. _cache documentation: ../cache/
dbshell dbshell
------- -------
@ -190,7 +190,7 @@ By default, the development server doesn't serve any static files for your site
you want to configure Django to serve static media, read the `serving static files`_ you want to configure Django to serve static media, read the `serving static files`_
documentation. documentation.
.. _serving static files: http://www.djangoproject.com/documentation/static_files/ .. _serving static files: ../static_files/
Turning off auto-reload Turning off auto-reload
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
@ -253,6 +253,8 @@ files are piped directly into the database after all of the models'
table-creation statements have been executed. Use this SQL hook to populate table-creation statements have been executed. Use this SQL hook to populate
tables with any necessary initial records, SQL functions or test data. tables with any necessary initial records, SQL functions or test data.
Note that the order in which the SQL files are processed is undefined.
sqlreset [appname appname ...] sqlreset [appname appname ...]
-------------------------------------- --------------------------------------

View File

@ -101,9 +101,9 @@ The "From:" header of the e-mail will be the value of the `SERVER_EMAIL setting`
This method exists for convenience and readability. This method exists for convenience and readability.
.. _ADMINS setting: http://www.djangoproject.com/documentation/settings/#admins .. _ADMINS setting: ../settings/#admins
.. _EMAIL_SUBJECT_PREFIX setting: http://www.djangoproject.com/documentation/settings/#email-subject-prefix .. _EMAIL_SUBJECT_PREFIX setting: ../settings/#email-subject-prefix
.. _SERVER_EMAIL setting: http://www.djangoproject.com/documentation/settings/#server-email .. _SERVER_EMAIL setting: ../settings/#server-email
mail_managers() function mail_managers() function
======================== ========================
@ -114,7 +114,7 @@ Here's the definition::
mail_managers(subject, message, fail_silently=False) mail_managers(subject, message, fail_silently=False)
.. _MANAGERS setting: http://www.djangoproject.com/documentation/settings/#managers .. _MANAGERS setting: ../settings/#managers
Examples Examples
======== ========

View File

@ -63,7 +63,7 @@ at any level -- database servers, caching servers or Web/application servers.
The framework cleanly separates components such as its database layer and The framework cleanly separates components such as its database layer and
application layer. And it ships with a simple-yet-powerful `cache framework`_. application layer. And it ships with a simple-yet-powerful `cache framework`_.
.. _`cache framework`: http://www.djangoproject.com/documentation/cache/ .. _`cache framework`: ../cache/
Who's behind this? Who's behind this?
------------------ ------------------
@ -191,7 +191,7 @@ Like we said: We're picky.
We've documented our philosophies on the `design philosophies page`_. We've documented our philosophies on the `design philosophies page`_.
.. _design philosophies page: http://www.djangoproject.com/documentation/design_philosophies/ .. _design philosophies page: ../design_philosophies/
Do you have any of those nifty "screencast" things? Do you have any of those nifty "screencast" things?
--------------------------------------------------- ---------------------------------------------------
@ -277,9 +277,9 @@ How do I get started?
run into trouble. run into trouble.
.. _`Download the code`: http://www.djangoproject.com/download/ .. _`Download the code`: http://www.djangoproject.com/download/
.. _`installation guide`: http://www.djangoproject.com/documentation/install/ .. _`installation guide`: ../install/
.. _tutorial: http://www.djangoproject.com/documentation/tutorial1/ .. _tutorial: ../tutorial1/
.. _documentation: http://www.djangoproject.com/documentation/ .. _documentation: ../
.. _ask questions: http://www.djangoproject.com/community/ .. _ask questions: http://www.djangoproject.com/community/
How do I fix the "install a later version of setuptools" error? How do I fix the "install a later version of setuptools" error?
@ -337,7 +337,7 @@ If you just want to play around and develop things on your local computer, use
the development Web server that comes with Django. Things should Just Work. the development Web server that comes with Django. Things should Just Work.
.. _WSGI: http://www.python.org/peps/pep-0333.html .. _WSGI: http://www.python.org/peps/pep-0333.html
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/ .. _How to use Django with FastCGI: ../fastcgi/
.. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements .. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
How do I install mod_python on Windows? How do I install mod_python on Windows?
@ -464,7 +464,7 @@ Can I use Django with a pre-existing database?
Yes. See `Integrating with a legacy database`_. Yes. See `Integrating with a legacy database`_.
.. _`Integrating with a legacy database`: http://www.djangoproject.com/documentation/legacy_databases/ .. _`Integrating with a legacy database`: ../legacy_databases/
If I make changes to a model, how do I update the database? If I make changes to a model, how do I update the database?
----------------------------------------------------------- -----------------------------------------------------------
@ -511,7 +511,7 @@ type, create an initial data file and put something like this in it::
As explained in the `SQL initial data file`_ documentation, this SQL file can As explained in the `SQL initial data file`_ documentation, this SQL file can
contain arbitrary SQL, so you can make any sorts of changes you need to make. contain arbitrary SQL, so you can make any sorts of changes you need to make.
.. _SQL initial data file: http://www.djangoproject.com/documentation/model_api/#providing-initial-sql-data .. _SQL initial data file: ../model_api/#providing-initial-sql-data
Why is Django leaking memory? Why is Django leaking memory?
----------------------------- -----------------------------
@ -592,7 +592,7 @@ My admin-site CSS and images showed up fine using the development server, but th
See `serving the admin files`_ in the "How to use Django with mod_python" See `serving the admin files`_ in the "How to use Django with mod_python"
documentation. documentation.
.. _serving the admin files: http://www.djangoproject.com/documentation/modpython/#serving-the-admin-files .. _serving the admin files: ../modpython/#serving-the-admin-files
My "list_filter" contains a ManyToManyField, but the filter doesn't display. My "list_filter" contains a ManyToManyField, but the filter doesn't display.
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
@ -630,7 +630,7 @@ site is built using semantic HTML and plenty of CSS hooks, so any changes you'd
like to make should be possible by editing the stylesheet. We've got a like to make should be possible by editing the stylesheet. We've got a
`guide to the CSS used in the admin`_ to get you started. `guide to the CSS used in the admin`_ to get you started.
.. _`guide to the CSS used in the admin`: http://www.djangoproject.com/documentation/admin_css/ .. _`guide to the CSS used in the admin`: ../admin_css/
How do I create users without having to edit password hashes? How do I create users without having to edit password hashes?
------------------------------------------------------------- -------------------------------------------------------------
@ -640,7 +640,7 @@ development version, where this problem was fixed on Aug. 4, 2006.
You can also use the Python API. See `creating users`_ for full info. You can also use the Python API. See `creating users`_ for full info.
.. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users .. _creating users: ../authentication/#creating-users
Contributing code Contributing code
================= =================
@ -651,7 +651,7 @@ How can I get started contributing code to Django?
Thanks for asking! We've written an entire document devoted to this question. Thanks for asking! We've written an entire document devoted to this question.
It's titled `Contributing to Django`_. It's titled `Contributing to Django`_.
.. _Contributing to Django: http://www.djangoproject.com/documentation/contributing/ .. _Contributing to Django: ../contributing/
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch? I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------

View File

@ -17,7 +17,7 @@ served with no startup time. Unlike mod_python (or `mod_perl`_), a FastCGI
process doesn't run inside the Web server process, but in a separate, process doesn't run inside the Web server process, but in a separate,
persistent process. persistent process.
.. _current preferred setup: http://www.djangoproject.com/documentation/modpython/ .. _current preferred setup: ../modpython/
.. _Apache: http://httpd.apache.org/ .. _Apache: http://httpd.apache.org/
.. _mod_python: http://www.modpython.org/ .. _mod_python: http://www.modpython.org/
.. _mod_perl: http://perl.apache.org/ .. _mod_perl: http://perl.apache.org/

View File

@ -29,8 +29,8 @@ To install the flatpages app, follow these steps:
to your MIDDLEWARE_CLASSES_ setting. to your MIDDLEWARE_CLASSES_ setting.
3. Run the command ``manage.py syncdb``. 3. Run the command ``manage.py syncdb``.
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps .. _INSTALLED_APPS: ../settings/#installed-apps
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes .. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
How it works How it works
============ ============
@ -63,9 +63,9 @@ resort.
For more on middleware, read the `middleware docs`_. For more on middleware, read the `middleware docs`_.
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id .. _SITE_ID: ../settings/#site-id
.. _RequestContext: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext .. _RequestContext: ../templates_python/#subclassing-context-djangocontext
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/ .. _middleware docs: ../middleware/
How to add, change and delete flatpages How to add, change and delete flatpages
======================================= =======================================
@ -84,9 +84,9 @@ Flatpages are represented by a standard `Django model`_, which lives in
`django/contrib/flatpages/models.py`_. You can access flatpage objects via the `django/contrib/flatpages/models.py`_. You can access flatpage objects via the
`Django database API`_. `Django database API`_.
.. _Django model: http://www.djangoproject.com/documentation/model_api/ .. _Django model: ../model_api/
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py .. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
.. _Django database API: http://www.djangoproject.com/documentation/db_api/ .. _Django database API: ../db_api/
Flatpage templates Flatpage templates
================== ==================

View File

@ -14,7 +14,7 @@ use the django.newforms system, which we have begun to document in the
If you have legacy form/manipulator code, read the "Migration plan" section in If you have legacy form/manipulator code, read the "Migration plan" section in
that document to understand how we're making the switch. that document to understand how we're making the switch.
.. _newforms documentation: http://www.djangoproject.com/documentation/newforms/ .. _newforms documentation: ../newforms/
Introduction Introduction
============ ============
@ -665,6 +665,6 @@ fails. If no message is passed in, a default message is used.
the executable specified in the ``JING_PATH`` setting (see the settings_ the executable specified in the ``JING_PATH`` setting (see the settings_
document for more details). document for more details).
.. _`generic views`: http://www.djangoproject.com/documentation/generic_views/ .. _`generic views`: ../generic_views/
.. _`models API`: http://www.djangoproject.com/documentation/model_api/ .. _`models API`: ../model_api/
.. _settings: http://www.djangoproject.com/documentation/settings/ .. _settings: ../settings/

View File

@ -71,7 +71,7 @@ are first evaluated, so if you want to pass in a QuerySet via
``extra_context`` that is always fresh you need to wrap it in a function or ``extra_context`` that is always fresh you need to wrap it in a function or
lambda that returns the QuerySet. lambda that returns the QuerySet.
.. _database API docs: http://www.djangoproject.com/documentation/db_api/ .. _database API docs: ../db_api/
"Simple" generic views "Simple" generic views
====================== ======================
@ -223,7 +223,7 @@ In addition to ``extra_context``, the template's context will be:
by ``date_field``. For example, if ``num_latest`` is ``10``, then by ``date_field``. For example, if ``num_latest`` is ``10``, then
``latest`` will be a list of the latest 10 objects in ``queryset``. ``latest`` will be a list of the latest 10 objects in ``queryset``.
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext .. _RequestContext docs: ../templates_python/#subclassing-context-djangocontext
``django.views.generic.date_based.archive_year`` ``django.views.generic.date_based.archive_year``
------------------------------------------------ ------------------------------------------------
@ -916,8 +916,8 @@ In addition to ``extra_context``, the template's context will be:
See the `manipulator and formfield documentation`_ for more information See the `manipulator and formfield documentation`_ for more information
about using ``FormWrapper`` objects in templates. about using ``FormWrapper`` objects in templates.
.. _authentication system: http://www.djangoproject.com/documentation/authentication/ .. _authentication system: ../authentication/
.. _manipulator and formfield documentation: http://www.djangoproject.com/documentation/forms/ .. _manipulator and formfield documentation: ../forms/
``django.views.generic.create_update.update_object`` ``django.views.generic.create_update.update_object``
---------------------------------------------------- ----------------------------------------------------

View File

@ -53,7 +53,7 @@ internationalization machinery. See the `documentation for USE_I18N`_.
You'll probably also want to remove ``'django.core.context_processors.i18n'`` You'll probably also want to remove ``'django.core.context_processors.i18n'``
from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting. from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
.. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n .. _documentation for USE_I18N: ../settings/#use-i18n
How to specify translation strings How to specify translation strings
================================== ==================================
@ -175,7 +175,7 @@ class, though::
verbose_name = _('my thing') verbose_name = _('my thing')
verbose_name_plural = _('mythings') verbose_name_plural = _('mythings')
.. _Django models: http://www.djangoproject.com/documentation/model_api/ .. _Django models: ../model_api/
Pluralization Pluralization
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -274,7 +274,7 @@ translation string. Example::
In this case, both the tag and the filter will see the already-translated In this case, both the tag and the filter will see the already-translated
string, so they don't need to be aware of translations. string, so they don't need to be aware of translations.
.. _Django templates: http://www.djangoproject.com/documentation/templates_python/ .. _Django templates: ../templates_python/
How to create language files How to create language files
============================ ============================
@ -394,7 +394,7 @@ That's it. Your translations are ready for use.
please let us know! See `Submitting and maintaining translations`_ for please let us know! See `Submitting and maintaining translations`_ for
the steps to take. the steps to take.
.. _Submitting and maintaining translations: http://www.djangoproject.com/documentation/contributing/ .. _Submitting and maintaining translations: ../contributing/
How Django discovers language preference How Django discovers language preference
======================================== ========================================
@ -472,7 +472,7 @@ Notes:
selection to German and English (and any sublanguage, like de-ch or selection to German and English (and any sublanguage, like de-ch or
en-us). en-us).
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages .. _LANGUAGES setting: ../settings/#languages
* If you define a custom ``LANGUAGES`` setting, as explained in the * If you define a custom ``LANGUAGES`` setting, as explained in the
previous bullet, it's OK to mark the languages as translation strings previous bullet, it's OK to mark the languages as translation strings
@ -530,10 +530,10 @@ Note that, with static (middleware-less) translation, the language is in
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's ``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
in ``request.LANGUAGE_CODE``. in ``request.LANGUAGE_CODE``.
.. _settings file: http://www.djangoproject.com/documentation/settings/ .. _settings file: ../settings/
.. _middleware documentation: http://www.djangoproject.com/documentation/middleware/ .. _middleware documentation: ../middleware/
.. _session: http://www.djangoproject.com/documentation/sessions/ .. _session: ../sessions/
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects .. _request object: ../request_response/#httprequest-objects
The ``set_language`` redirect view The ``set_language`` redirect view
================================== ==================================
@ -599,7 +599,7 @@ message file. The choice is yours.
of the settings file to determine this, and a settings file doesn't exist of the settings file to determine this, and a settings file doesn't exist
if you're manually configuring your settings.) if you're manually configuring your settings.)
.. _settings documentation: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable .. _settings documentation: ../settings/#using-settings-without-the-django-settings-module-environment-variable
All message file repositories are structured the same way. They are: All message file repositories are structured the same way. They are:

View File

@ -38,7 +38,7 @@ each platform.
.. _Apache: http://httpd.apache.org/ .. _Apache: http://httpd.apache.org/
.. _mod_python: http://www.modpython.org/ .. _mod_python: http://www.modpython.org/
.. _WSGI: http://www.python.org/peps/pep-0333.html .. _WSGI: http://www.python.org/peps/pep-0333.html
.. _How to use Django with mod_python: http://www.djangoproject.com/documentation/modpython/ .. _How to use Django with mod_python: ../modpython/
.. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements .. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
Get your database running Get your database running
@ -113,14 +113,14 @@ latest bug fixes and improvements, follow these instructions:
svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django
4. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your 3. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts`` system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
(Windows). This step simply lets you type ``django-admin.py`` from within (Windows). This step simply lets you type ``django-admin.py`` from within
any directory, rather than having to qualify the command with the full path any directory, rather than having to qualify the command with the full path
to the file. to the file.
You *don't* have to run ``python setup.py install``, because that command You *don't* have to run ``python setup.py install``, because that command
takes care of steps 3 and 4 for you. takes care of steps 2 and 3 for you.
When you want to update your copy of the Django source code, just run the When you want to update your copy of the Django source code, just run the
command ``svn update`` from within the ``django`` directory. When you do this, command ``svn update`` from within the ``django`` directory. When you do this,

View File

@ -9,7 +9,7 @@ utilities to automate as much of this process as possible.
This document assumes you know the Django basics, as covered in the This document assumes you know the Django basics, as covered in the
`official tutorial`_. `official tutorial`_.
.. _official tutorial: http://www.djangoproject.com/documentation/tutorial1/ .. _official tutorial: ../tutorial1/
Give Django your database parameters Give Django your database parameters
==================================== ====================================
@ -25,13 +25,13 @@ what the name of the database is. Do that by editing these settings in your
* `DATABASE_HOST`_ * `DATABASE_HOST`_
* `DATABASE_PORT`_ * `DATABASE_PORT`_
.. _settings file: http://www.djangoproject.com/documentation/settings/ .. _settings file: ../settings/
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name .. _DATABASE_NAME: ../settings/#database-name
.. _DATABASE_ENGINE: http://www.djangoproject.com/documentation/settings/#database-engine .. _DATABASE_ENGINE: ../settings/#database-engine
.. _DATABASE_USER: http://www.djangoproject.com/documentation/settings/#database-user .. _DATABASE_USER: ../settings/#database-user
.. _DATABASE_PASSWORD: http://www.djangoproject.com/documentation/settings/#database-password .. _DATABASE_PASSWORD: ../settings/#database-password
.. _DATABASE_HOST: http://www.djangoproject.com/documentation/settings/#database-host .. _DATABASE_HOST: ../settings/#database-host
.. _DATABASE_PORT: http://www.djangoproject.com/documentation/settings/#database-port .. _DATABASE_PORT: ../settings/#database-port
Auto-generate the models Auto-generate the models
======================== ========================
@ -52,7 +52,7 @@ Once you've cleaned up your models, name the file ``models.py`` and put it in
the Python package that holds your app. Then add the app to your the Python package that holds your app. Then add the app to your
``INSTALLED_APPS`` setting. ``INSTALLED_APPS`` setting.
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin.py documentation: ../django_admin/
Install the core Django tables Install the core Django tables
============================== ==============================

View File

@ -47,7 +47,7 @@ Enables site-wide cache. If this is enabled, each Django-powered page will be
cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See
the `cache documentation`_. the `cache documentation`_.
.. _`cache documentation`: http://www.djangoproject.com/documentation/cache/#the-per-site-cache .. _`cache documentation`: ../cache/#the-per-site-cache
django.middleware.common.CommonMiddleware django.middleware.common.CommonMiddleware
----------------------------------------- -----------------------------------------
@ -106,9 +106,10 @@ django.middleware.http.SetRemoteAddrFromForwardedFor
**New in Django development version** **New in Django development version**
Sets ``request['REMOTE_ADDR']`` based on ``request.['HTTP_X_FORWARDED_FOR']``, Sets ``request.META['REMOTE_ADDR']`` based on
if the latter is set. This is useful if you're sitting behind a reverse proxy ``request.META['HTTP_X_FORWARDED_FOR']``, if the latter is set. This is useful
that causes each request's ``REMOTE_ADDR`` to be set to ``127.0.0.1``. if you're sitting behind a reverse proxy that causes each request's
``REMOTE_ADDR`` to be set to ``127.0.0.1``.
**Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're **Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're
not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do
@ -122,7 +123,7 @@ django.contrib.sessions.middleware.SessionMiddleware
Enables session support. See the `session documentation`_. Enables session support. See the `session documentation`_.
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/ .. _`session documentation`: ../sessions/
django.contrib.auth.middleware.AuthenticationMiddleware django.contrib.auth.middleware.AuthenticationMiddleware
------------------------------------------------------- -------------------------------------------------------
@ -130,7 +131,7 @@ django.contrib.auth.middleware.AuthenticationMiddleware
Adds the ``user`` attribute, representing the currently-logged-in user, to Adds the ``user`` attribute, representing the currently-logged-in user, to
every incoming ``HttpRequest`` object. See `Authentication in Web requests`_. every incoming ``HttpRequest`` object. See `Authentication in Web requests`_.
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests .. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
django.middleware.transaction.TransactionMiddleware django.middleware.transaction.TransactionMiddleware
--------------------------------------------------- ---------------------------------------------------
@ -146,7 +147,7 @@ the same transaction control as the view functions.
See the `transaction management documentation`_. See the `transaction management documentation`_.
.. _`transaction management documentation`: http://www.djangoproject.com/documentation/transactions/ .. _`transaction management documentation`: ../transactions/
Writing your own middleware Writing your own middleware
=========================== ===========================

View File

@ -875,6 +875,10 @@ the relationship should work. All are optional:
relationship, allowing ``ManyToMany`` relationships to be relationship, allowing ``ManyToMany`` relationships to be
non-symmetrical. non-symmetrical.
``db_table`` The name of the table to create for storing the many-to-many
data. If this is not provided, Django will assume a default
name based upon the names of the two tables being joined.
======================= ============================================================ ======================= ============================================================
One-to-one relationships One-to-one relationships
@ -1268,6 +1272,24 @@ A few special cases to note about ``list_display``:
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name) return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
colored_name.allow_tags = True colored_name.allow_tags = True
* If the string given is a method of the model that returns True or False
Django will display a pretty "on" or "off" icon if you give the method a
``boolean`` attribute whose value is ``True``.
Here's a full example model::
class Person(models.Model):
first_name = models.CharField(maxlength=50)
birthday = models.DateField()
class Admin:
list_display = ('name', 'born_in_fifties')
def born_in_fifties(self):
return self.birthday.strftime('%Y')[:3] == 5
born_in_fifties.boolean = True
* The ``__str__()`` method is just as valid in ``list_display`` as any * The ``__str__()`` method is just as valid in ``list_display`` as any
other model method, so it's perfectly OK to do this:: other model method, so it's perfectly OK to do this::
@ -1390,7 +1412,10 @@ This should be set to a list of field names that will be searched whenever
somebody submits a search query in that text box. somebody submits a search query in that text box.
These fields should be some kind of text field, such as ``CharField`` or These fields should be some kind of text field, such as ``CharField`` or
``TextField``. ``TextField``. You can also perform a related lookup on a ``ForeignKey`` with
the lookup API "follow" notation::
search_fields = ['foreign_key__related_fieldname']
When somebody does a search in the admin search box, Django splits the search When somebody does a search in the admin search box, Django splits the search
query into words and returns all objects that contain each of the words, case query into words and returns all objects that contain each of the words, case

View File

@ -20,7 +20,7 @@ You may also be interested in `How to use Django with FastCGI`_.
.. _mod_perl: http://perl.apache.org/ .. _mod_perl: http://perl.apache.org/
.. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html .. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html
.. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html .. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/ .. _How to use Django with FastCGI: ../fastcgi/
Basic configuration Basic configuration
=================== ===================

View File

@ -2,9 +2,9 @@
The newforms library The newforms library
==================== ====================
``django.newforms`` is a new replacement for ``django.forms``, the old Django ``django.newforms`` is Django's fantastic new form-handling library. It's a
form/manipulator/validation framework. This document explains how to use this replacement for ``django.forms``, the old form/manipulator/validation
new form library. framework. This document explains how to use this new library.
Migration plan Migration plan
============== ==============
@ -51,7 +51,7 @@ too messy. The choice is yours.
Overview Overview
======== ========
As the ``django.forms`` ("manipulators") system before it, ``django.newforms`` As with the ``django.forms`` ("manipulators") system before it, ``django.newforms``
is intended to handle HTML form display, validation and redisplay. It's what 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. you use if you want to perform server-side validation for an HTML form.
@ -74,7 +74,9 @@ The library deals with these concepts:
The library is decoupled from the other Django components, such as the database The library is decoupled from the other Django components, such as the database
layer, views and templates. It relies only on Django settings, a couple of layer, views and templates. It relies only on Django settings, a couple of
``django.utils`` helper functions and Django's internationalization system. ``django.utils`` helper functions and Django's internationalization hooks (but
you're not required to be using internationalization features to use this
library).
Form objects Form objects
============ ============
@ -83,8 +85,8 @@ 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 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 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 Django database models. In this section, we'll iteratively develop a form
object that you might to implement "contact me" functionality on your personal object that you might use to implement "contact me" functionality on your
Web site. personal Web site.
Start with this basic ``Form`` subclass, which we'll call ``ContactForm``:: Start with this basic ``Form`` subclass, which we'll call ``ContactForm``::
@ -101,11 +103,192 @@ fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain
the different types of fields -- e.g., ``CharField`` and ``EmailField`` -- the different types of fields -- e.g., ``CharField`` and ``EmailField`` --
shortly. shortly.
Creating ``Form`` instances
---------------------------
A ``Form`` instance is either **bound** or **unbound** to a set of data.
* If it's **bound** to a set of data, it's capable of validating that data
and rendering the form as HTML with the data displayed in the HTML.
* If it's **unbound**, it cannot do validation (because there's no data to
validate!), but it can still render the blank form as HTML.
To create an unbound ``Form`` instance, simply instantiate the class::
>>> f = ContactForm()
To bind data to a form, pass the data as a dictionary as the first parameter to
your ``Form`` class constructor::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
In this dictionary, the keys are the field names, which correspond to the
attributes in your ``Form`` class. The values are the data you're trying
to validate. These will usually be strings, but there's no requirement that
they be strings; the type of data you pass depends on the ``Field``, as we'll
see in a moment.
If you need to distinguish between bound and unbound form instances at runtime,
check the value of the form's ``is_bound`` attribute::
>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({'subject': 'hello'})
>>> f.is_bound
True
Note that passing an empty dictionary creates a *bound* form with empty data::
>>> f = ContactForm({})
>>> f.is_bound
True
If you have a bound ``Form`` instance and want to change the data somehow, or
if you want to bind an unbound ``Form`` instance to some data, create another
``Form`` instance. There is no way to change data in a ``Form`` instance. Once
a ``Form`` instance has been created, you should consider its data immutable,
whether it has data or not.
Using forms to validate data
----------------------------
The primary task of a ``Form`` object is to validate data. With a bound
``Form`` instance, call the ``is_valid()`` method to run validation and return
a boolean designating whether the data was valid::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True
Let's try with some invalid data. In this case, ``subject`` is blank (an error,
because all fields are required by default) and ``sender`` is not a valid
e-mail address::
>>> data = {'subject': '',
... 'message': 'Hi there',
... 'sender': 'invalid e-mail address',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
Access the ``Form`` attribute ``errors`` to get a dictionary of error messages::
>>> f.errors
{'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']}
In this dictionary, the keys are the field names, and the values are lists of
Unicode strings representing the error messages. The error messages are stored
in lists because a field can have multiple error messages.
You can access ``errors`` without having to call ``is_valid()`` first. The
form's data will be validated the first time either you call ``is_valid()`` or
access ``errors``.
Behavior of unbound forms
~~~~~~~~~~~~~~~~~~~~~~~~~
It's meaningless to validate a form with no data, but, for the record, here's
what happens with unbound forms::
>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}
Accessing "clean" data
----------------------
Each ``Field`` in a ``Form`` class is responsible not only for validating data,
but also for "cleaning" it -- normalizing it to a consistent format. This is a
nice feature, because it allows data for a particular field to be input in
a variety of ways, always resulting in consistent output.
For example, ``DateField`` normalizes input into a Python ``datetime.date``
object. Regardless of whether you pass it a string in the format
``'1994-07-15'``, a ``datetime.date`` object or a number of other formats,
``DateField`` will always normalize it to a ``datetime.date`` object as long as
it's valid.
Once you've created a ``Form`` instance with a set of data and validated it,
you can access the clean data via the ``clean_data`` attribute of the ``Form``
object::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.clean_data
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
Note that any text-based field -- such as ``CharField`` or ``EmailField`` --
always cleans the input into a Unicode string. We'll cover the encoding
implications later in this document.
If your data does *not* validate, your ``Form`` instance will not have a
``clean_data`` attribute::
>>> data = {'subject': '',
... 'message': 'Hi there',
... 'sender': 'invalid e-mail address',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.clean_data
Traceback (most recent call last):
...
AttributeError: 'ContactForm' object has no attribute 'clean_data'
``clean_data`` will always *only* contain a key for fields defined in the
``Form``, even if you pass extra data when you define the ``Form``. In this
example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
but ``clean_data`` contains only the form's fields::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True,
... 'extra_field_1': 'foo',
... 'extra_field_2': 'bar',
... 'extra_field_3': 'baz'}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.clean_data # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
Behavior of unbound forms
~~~~~~~~~~~~~~~~~~~~~~~~~
It's meaningless to request "clean" data in a form with no data, but, for the
record, here's what happens with unbound forms::
>>> f = ContactForm()
>>> f.clean_data
Traceback (most recent call last):
...
AttributeError: 'ContactForm' object has no attribute 'clean_data'
Outputting forms as HTML Outputting forms as HTML
------------------------ ------------------------
The first thing we can do with this is output it as HTML. To do so, instantiate The second task of a ``Form`` object is to render itself as HTML. To do so,
it and ``print`` it:: simply ``print`` it::
>>> f = ContactForm() >>> f = ContactForm()
>>> print f >>> print f
@ -114,6 +297,23 @@ it and ``print`` it::
<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_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> <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
If the form is bound to data, the HTML output will include that data
appropriately. For example, if a field is represented by an
``<input type="text">``, the data will be in the ``value`` attribute. If a
field is represented by an ``<input type="checkbox">``, then that HTML will
include ``checked="checked"`` if appropriate::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> print f
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
This default output is a two-column HTML table, with a ``<tr>`` for each field. This default output is a two-column HTML table, with a ``<tr>`` for each field.
Notice the following: Notice the following:
@ -125,13 +325,13 @@ Notice the following:
``EmailField`` are represented by an ``<input type="text">``. ``EmailField`` are represented by an ``<input type="text">``.
``BooleanField`` is represented by an ``<input type="checkbox">``. Note ``BooleanField`` is represented by an ``<input type="checkbox">``. Note
these are merely sensible defaults; you can specify which HTML to use for these are merely sensible defaults; you can specify which HTML to use for
a given field by using ``widgets``, which we'll explain shortly. a given field by using widgets, which we'll explain shortly.
* The HTML ``name`` for each tag is taken directly from its attribute name * The HTML ``name`` for each tag is taken directly from its attribute name
in the ``ContactForm`` class. in the ``ContactForm`` class.
* The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and * The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and
``'CC myself:'`` is generated from the field name by converting all ``'Cc myself:'`` is generated from the field name by converting all
underscores to spaces and upper-casing the first letter. Again, note underscores to spaces and upper-casing the first letter. Again, note
these are merely sensible defaults; you can also specify labels manually. these are merely sensible defaults; you can also specify labels manually.
@ -286,6 +486,37 @@ example, in the ``ContactForm`` example, the fields are defined in the order
``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML ``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML
output, just change the order in which those fields are listed in the class. output, just change the order in which those fields are listed in the class.
How errors are displayed
~~~~~~~~~~~~~~~~~~~~~~~~
If you render a bound ``Form`` object, the act of rendering will automatically
run the form's validation if it hasn't already happened, and the HTML output
will include the validation errors as a ``<ul>`` near the field. The particular
positioning of the error messages depends on the output method you're using::
>>> data = {'subject': '',
... 'message': 'Hi there',
... 'sender': 'invalid e-mail address',
... 'cc_myself': True}
>>> f = ContactForm(data, auto_id=False)
>>> print f.as_table()
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr>
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul><input type="text" name="sender" value="invalid e-mail address" /></td></tr>
<tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr>
>>> print f.as_ul()
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" value="Hi there" /></li>
<li><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul>Sender: <input type="text" name="sender" value="invalid e-mail address" /></li>
<li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li>
>>> print f.as_p()
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<p><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul></p>
<p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
More granular output More granular output
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
@ -322,6 +553,271 @@ The field-specific output honors the form object's ``auto_id`` setting::
>>> print f['message'] >>> print f['message']
<input type="text" name="message" id="id_message" /> <input type="text" name="message" id="id_message" />
For a field's list of errors, access the field's ``errors`` attribute. This
is a list-like object that is displayed as an HTML ``<ul>`` when printed::
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
>>> f = ContactForm(data, auto_id=False)
>>> print f['message']
<input type="text" name="message" />
>>> f['message'].errors
[u'This field is required.']
>>> print f['message'].errors
<ul class="errorlist"><li>This field is required.</li></ul>
>>> f['subject'].errors
[]
>>> print f['subject'].errors
>>> str(f['subject'].errors)
''
Fields
======
When you create a ``Form`` class, the most important part is defining the
fields of the form. Each field has custom validation logic, along with a few
other hooks.
Although the primary way you'll use ``Field`` classes is in ``Form`` classes,
you can also instantiate them and use them directly to get a better idea of
how they work. Each ``Field`` instance has a ``clean()`` method, which takes
a single argument and either raises a ``django.newforms.ValidationError``
exception or returns the clean value::
>>> f = forms.EmailField()
>>> f.clean('foo@example.com')
u'foo@example.com'
>>> f.clean(u'foo@example.com')
u'foo@example.com'
>>> f.clean('invalid e-mail address')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid e-mail address.']
If you've used Django's old forms/validation framework, take care in noticing
this ``ValidationError`` is different than the previous ``ValidationError``.
This one lives at ``django.newforms.ValidationError`` rather than
``django.core.validators.ValidationError``.
Core field arguments
--------------------
Each ``Field`` class constructor takes at least these arguments. Some
``Field`` classes take additional, field-specific arguments, but the following
should *always* be available:
``required``
~~~~~~~~~~~~
By default, each ``Field`` class assumes the value is required, so if you pass
an empty value -- either ``None`` or the empty string (``""``) -- then
``clean()`` will raise a ``ValidationError`` exception::
>>> f = forms.CharField()
>>> f.clean('foo')
u'foo'
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(' ')
u' '
>>> f.clean(0)
u'0'
>>> f.clean(True)
u'True'
>>> f.clean(False)
u'False'
To specify that a field is *not* required, pass ``required=False`` to the
``Field`` constructor::
>>> f = forms.CharField(required=False)
>>> f.clean('foo')
u'foo'
>>> f.clean('')
u''
>>> f.clean(None)
u''
>>> f.clean(0)
u'0'
>>> f.clean(True)
u'True'
>>> f.clean(False)
u'False'
If a ``Field`` has ``required=False`` and you pass ``clean()`` an empty value,
then ``clean()`` will return a *normalized* empty value rather than raising
``ValidationError``. For ``CharField``, this will be a Unicode empty string.
For other ``Field`` classes, it might be ``None``. (This varies from field to
field.)
``label``
~~~~~~~~~
The ``label`` argument lets you specify the "human-friendly" label for this
field. This is used when the ``Field`` is displayed in a ``Form``.
As explained in _`Outputting forms as HTML` above, the default label for a
``Field`` is generated from the field name by converting all underscores to
spaces and upper-casing the first letter. Specify ``label`` if that default
behavior doesn't result in an adequate label.
Here's a full example ``Form`` that implements ``label`` for two of its fields.
We've specified ``auto_id=False`` to simplify the output::
>>> class CommentForm(forms.Form):
... name = forms.CharField(label='Your name')
... url = forms.URLField(label='Your Web site', required=False)
... comment = forms.CharField()
>>> f = CommentForm(auto_id=False)
>>> print f
<tr><th>Your name:</th><td><input type="text" name="name" /></td></tr>
<tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
``initial``
~~~~~~~~~~~
The ``initial`` argument lets you specify the initial value to use when
rendering this ``Field`` in an unbound ``Form``.
The use-case for this is when you want to display an "empty" form in which a
field is initialized to a particular value. For example::
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial='Your name')
... url = forms.URLField(initial='http://')
... comment = forms.CharField()
>>> f = CommentForm(auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
You may be thinking, why not just pass a dictionary of the initial values as
data when displaying the form? Well, if you do that, you'll trigger validation,
and the HTML output will include any validation errors::
>>> class CommentForm(forms.Form):
... name = forms.CharField()
... url = forms.URLField()
... comment = forms.CharField()
>>> default_data = {'name': 'Your name', 'url': 'http://'}
>>> f = CommentForm(default_data, auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
<tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr>
<tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr>
This is why ``initial`` values are only displayed for unbound forms. For bound
forms, the HTML output will use the bound data.
Also note that ``initial`` values are *not* used as "fallback" data in
validation if a particular field's value is not given. ``initial`` values are
*only* intended for initial form display::
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial='Your name')
... url = forms.URLField(initial='http://')
... comment = forms.CharField()
>>> data = {'name': '', 'url': '', 'comment': 'Foo'}
>>> f = CommentForm(data)
>>> f.is_valid()
False
# The form does *not* fall back to using the initial values.
>>> f.errors
{'url': [u'This field is required.'], 'name': [u'This field is required.']}
``widget``
~~~~~~~~~~
The ``widget`` argument lets you specify a ``Widget`` class to use when
rendering this ``Field``. See _`Widgets` below for more information.
``help_text``
~~~~~~~~~~~~~
The ``help_text`` argument lets you specify descriptive text for this
``Field``. If you provide ``help_text``, it will be displayed next to the
``Field`` when the ``Field`` is rendered in a ``Form``.
Here's a full example ``Form`` that implements ``help_text`` for two of its
fields. We've specified ``auto_id=False`` to simplify the output::
>>> class HelpTextContactForm(forms.Form):
... subject = forms.CharField(max_length=100, help_text='100 characters max.')
... message = forms.CharField()
... sender = forms.EmailField(help_text='A valid e-mail address, please.')
... cc_myself = forms.BooleanField()
>>> f = HelpTextContactForm(auto_id=False)
>>> print f.as_table()
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
<tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid e-mail address, please.</td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print f.as_ul()
<li>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</li>
<li>Message: <input type="text" name="message" /></li>
<li>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print f.as_p()
<p>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</p>
<p>Message: <input type="text" name="message" /></p>
<p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
Dynamic initial values
----------------------
The ``initial`` argument to ``Field`` (explained above) lets you hard-code the
initial value for a ``Field`` -- but what if you want to declare the initial
value at runtime? For example, you might want to fill in a ``username`` field
with the username of the current session.
To accomplish this, use the ``initial`` argument to a ``Form``. This argument,
if given, should be a dictionary mapping field names to initial values. Only
include the fields for which you're specifying an initial value; it's not
necessary to include every field in your form. For example::
>>> class CommentForm(forms.Form):
... name = forms.CharField()
... url = forms.URLField()
... comment = forms.CharField()
>>> f = CommentForm(initial={'name': 'your username'}, auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="your username" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
>>> f = CommentForm(initial={'name': 'another username'}, auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="another username" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
Just like the ``initial`` parameter to ``Field``, these values are only
displayed for unbound forms, and they're not used as fallback values if a
particular value isn't provided.
Finally, note that if a ``Field`` defines ``initial`` *and* you include
``initial`` when instantiating the ``Form``, then the latter ``initial`` will
have precedence. In this example, ``initial`` is provided both at the field
level and at the form instance level, and the latter gets precedence::
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial='class')
... url = forms.URLField()
... comment = forms.CharField()
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="instance" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
More coming soon More coming soon
================ ================
@ -333,8 +829,5 @@ what's possible.
If you're really itching to learn and use this library, please be patient. If you're really itching to learn and use this library, please be patient.
We're working hard on finishing both the code and documentation. We're working hard on finishing both the code and documentation.
Using forms with templates Widgets
========================== =======
Using forms in views
====================

View File

@ -24,7 +24,7 @@ and Django's ``HttpResponse`` objects are file-like objects.
For more information on the CSV library, see the `CSV library docs`_. For more information on the CSV library, see the `CSV library docs`_.
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/ .. _Request and response objects: ../request_response/
.. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html .. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html
Here's an example:: Here's an example::
@ -115,5 +115,5 @@ a line of CSV for each row. It uses the `addslashes template filter`_ to ensure
there aren't any problems with quotes. If you can be certain your data doesn't there aren't any problems with quotes. If you can be certain your data doesn't
have single or double quotes in it, you can remove the ``addslashes`` filters. have single or double quotes in it, you can remove the ``addslashes`` filters.
.. _Django template system: http://www.djangoproject.com/documentation/templates/ .. _Django template system: ../templates/
.. _addslashes template filter: http://www.djangoproject.com/documentation/templates/#addslashes .. _addslashes template filter: ../templates/#addslashes

View File

@ -43,7 +43,7 @@ objects.
For more information on ``HttpResponse`` objects, see For more information on ``HttpResponse`` objects, see
`Request and response objects`_. `Request and response objects`_.
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/ .. _Request and response objects: ../request_response/
Here's a "Hello World" example:: Here's a "Hello World" example::

View File

@ -11,7 +11,7 @@ understand how Django works, but this isn't intended to be a tutorial or
reference. Please see our more-detailed Django documentation_ when you're ready reference. Please see our more-detailed Django documentation_ when you're ready
to start a project. to start a project.
.. _documentation: http://www.djangoproject.com/documentation/ .. _documentation: ../
Design your model Design your model
================= =================

View File

@ -15,8 +15,8 @@ To install the redirects app, follow these steps:
to your MIDDLEWARE_CLASSES_ setting. to your MIDDLEWARE_CLASSES_ setting.
3. Run the command ``manage.py syncdb``. 3. Run the command ``manage.py syncdb``.
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps .. _INSTALLED_APPS: ../settings/#installed-apps
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes .. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
How it works How it works
============ ============
@ -46,8 +46,8 @@ resort.
For more on middleware, read the `middleware docs`_. For more on middleware, read the `middleware docs`_.
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id .. _SITE_ID: ../settings/#site-id
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/ .. _middleware docs: ../middleware/
How to add, change and delete redirects How to add, change and delete redirects
======================================= =======================================
@ -66,6 +66,6 @@ Redirects are represented by a standard `Django model`_, which lives in
`django/contrib/redirects/models.py`_. You can access redirect `django/contrib/redirects/models.py`_. You can access redirect
objects via the `Django database API`_. objects via the `Django database API`_.
.. _Django model: http://www.djangoproject.com/documentation/model_api/ .. _Django model: ../model_api/
.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py .. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
.. _Django database API: http://www.djangoproject.com/documentation/db_api/ .. _Django database API: ../db_api/

View File

@ -117,14 +117,14 @@ All attributes except ``session`` should be considered read-only.
``AuthenticationMiddleware`` activated. For more, see ``AuthenticationMiddleware`` activated. For more, see
`Authentication in Web requests`_. `Authentication in Web requests`_.
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests .. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
``session`` ``session``
A readable-and-writable, dictionary-like object that represents the current A readable-and-writable, dictionary-like object that represents the current
session. This is only available if your Django installation has session session. This is only available if your Django installation has session
support activated. See the `session documentation`_ for full details. support activated. See the `session documentation`_ for full details.
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/ .. _`session documentation`: ../sessions/
``raw_post_data`` ``raw_post_data``
The raw HTTP POST data. This is only useful for advanced processing. Use The raw HTTP POST data. This is only useful for advanced processing. Use

View File

@ -27,7 +27,7 @@ If you don't want to use sessions, you might as well remove the
``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'`` ``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
from your ``INSTALLED_APPS``. It'll save you a small bit of overhead. from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
.. _middleware: http://www.djangoproject.com/documentation/middleware/ .. _middleware: ../middleware/
Using sessions in views Using sessions in views
======================= =======================
@ -290,7 +290,7 @@ Whether to save the session data on every request. If this is ``False``
(default), then the session data will only be saved if it has been modified -- (default), then the session data will only be saved if it has been modified --
that is, if any of its dictionary values have been assigned or deleted. that is, if any of its dictionary values have been assigned or deleted.
.. _Django settings: http://www.djangoproject.com/documentation/settings/ .. _Django settings: ../settings/
Technical details Technical details
================= =================

View File

@ -59,7 +59,7 @@ Use the ``--settings`` command-line argument to specify the settings manually::
django-admin.py runserver --settings=mysite.settings django-admin.py runserver --settings=mysite.settings
.. _django-admin.py: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin.py: ../django_admin/
On the server (mod_python) On the server (mod_python)
-------------------------- --------------------------
@ -75,7 +75,7 @@ settings file to use. Do that with ``SetEnv``::
Read the `Django mod_python documentation`_ for more information. Read the `Django mod_python documentation`_ for more information.
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/ .. _Django mod_python documentation: ../modpython/
Default settings Default settings
================ ================
@ -102,7 +102,7 @@ between the current settings file and Django's default settings.
For more, see the `diffsettings documentation`_. For more, see the `diffsettings documentation`_.
.. _diffsettings documentation: http://www.djangoproject.com/documentation/django_admin/#diffsettings .. _diffsettings documentation: ../django_admin/#diffsettings
Using settings in Python code Using settings in Python code
============================= =============================
@ -157,13 +157,13 @@ ABSOLUTE_URL_OVERRIDES
Default: ``{}`` (Empty dictionary) Default: ``{}`` (Empty dictionary)
A dictionary mapping ``"app_label.module_name"`` strings to functions that take A dictionary mapping ``"app_label.model_name"`` strings to functions that take
a model object and return its URL. This is a way of overriding a model object and return its URL. This is a way of overriding
``get_absolute_url()`` methods on a per-installation basis. Example:: ``get_absolute_url()`` methods on a per-installation basis. Example::
ABSOLUTE_URL_OVERRIDES = { ABSOLUTE_URL_OVERRIDES = {
'blogs.blogs': lambda o: "/blogs/%s/" % o.slug, 'blogs.Weblog': lambda o: "/blogs/%s/" % o.slug,
'news.stories': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug), 'news.Story': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
} }
ADMIN_FOR ADMIN_FOR
@ -306,7 +306,7 @@ pages -- and, possibly, by other parts of the system. See
See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT. See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now .. _allowed date format strings: ../templates/#now
DATETIME_FORMAT DATETIME_FORMAT
--------------- ---------------
@ -319,7 +319,7 @@ pages -- and, possibly, by other parts of the system. See
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT. See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now .. _allowed date format strings: ../templates/#now
DEBUG DEBUG
----- -----
@ -433,7 +433,7 @@ A tuple of strings designating all applications that are enabled in this Django
installation. Each string should be a full Python path to a Python package that installation. Each string should be a full Python path to a Python package that
contains a Django application, as created by `django-admin.py startapp`_. contains a Django application, as created by `django-admin.py startapp`_.
.. _django-admin.py startapp: http://www.djangoproject.com/documentation/django_admin/#startapp-appname .. _django-admin.py startapp: ../django_admin/#startapp-appname
INTERNAL_IPS INTERNAL_IPS
------------ ------------
@ -464,7 +464,7 @@ A string representing the language code for this installation. This should be
in standard language format. For example, U.S. English is ``"en-us"``. See the in standard language format. For example, U.S. English is ``"en-us"``. See the
`internationalization docs`_. `internationalization docs`_.
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/ .. _internationalization docs: ../i18n/
LANGUAGES LANGUAGES
--------- ---------
@ -557,6 +557,11 @@ Default: ``''`` (Empty string)
URL that handles the media served from ``MEDIA_ROOT``. URL that handles the media served from ``MEDIA_ROOT``.
Example: ``"http://media.lawrence.com"`` Example: ``"http://media.lawrence.com"``
Note that this should have a trailing slash if it has a path component.
Good: ``"http://www.example.com/static/"``
Bad: ``"http://www.example.com/static"``
MIDDLEWARE_CLASSES MIDDLEWARE_CLASSES
------------------ ------------------
@ -612,7 +617,7 @@ Default: Not defined
A string representing the full Python import path to your root URLconf. For example: A string representing the full Python import path to your root URLconf. For example:
``"mydjangoapps.urls"``. See `How Django processes a request`_. ``"mydjangoapps.urls"``. See `How Django processes a request`_.
.. _How Django processes a request: http://www.djangoproject.com/documentation/url_dispatch/#how-django-processes-a-request .. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request
SECRET_KEY SECRET_KEY
---------- ----------
@ -704,7 +709,7 @@ and a single database can manage content for multiple sites.
See the `site framework docs`_. See the `site framework docs`_.
.. _site framework docs: http://www.djangoproject.com/documentation/sites/ .. _site framework docs: ../sites/
TEMPLATE_CONTEXT_PROCESSORS TEMPLATE_CONTEXT_PROCESSORS
--------------------------- ---------------------------
@ -760,7 +765,7 @@ Default: ``''`` (Empty string)
Output, as a string, that the template system should use for invalid (e.g. Output, as a string, that the template system should use for invalid (e.g.
misspelled) variables. See `How invalid variables are handled`_. misspelled) variables. See `How invalid variables are handled`_.
.. _How invalid variables are handled: http://www.djangoproject.com/documentation/templates_python/#how-invalid-variables-are-handled .. _How invalid variables are handled: ../templates_python/#how-invalid-variables-are-handled
TEST_RUNNER TEST_RUNNER
----------- -----------
@ -798,7 +803,7 @@ pages -- and, possibly, by other parts of the system. See
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and
MONTH_DAY_FORMAT. MONTH_DAY_FORMAT.
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now .. _allowed date format strings: ../templates/#now
TIME_ZONE TIME_ZONE
--------- ---------
@ -868,11 +873,11 @@ Different locales have different formats. For example, U.S. English would say
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT, See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
TIME_FORMAT and MONTH_DAY_FORMAT. TIME_FORMAT and MONTH_DAY_FORMAT.
.. _cache docs: http://www.djangoproject.com/documentation/cache/ .. _cache docs: ../cache/
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/ .. _middleware docs: ../middleware/
.. _session docs: http://www.djangoproject.com/documentation/sessions/ .. _session docs: ../sessions/
.. _See available choices: http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE .. _See available choices: http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
.. _template documentation: http://www.djangoproject.com/documentation/templates_python/ .. _template documentation: ../templates_python/
Creating your own settings Creating your own settings
========================== ==========================

View File

@ -23,8 +23,8 @@ you express this information in Python code.
It works much like Django's `syndication framework`_. To create a sitemap, just It works much like Django's `syndication framework`_. To create a sitemap, just
write a ``Sitemap`` class and point to it in your URLconf_. write a ``Sitemap`` class and point to it in your URLconf_.
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/ .. _syndication framework: ../syndication/
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Installation Installation
============ ============
@ -41,9 +41,9 @@ To install the sitemap app, follow these steps:
reason it needs to go into ``INSTALLED_APPS`` is so that the reason it needs to go into ``INSTALLED_APPS`` is so that the
``load_template_source`` template loader can find the default templates.) ``load_template_source`` template loader can find the default templates.)
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps .. _INSTALLED_APPS: ../settings/#installed-apps
.. _TEMPLATE_LOADERS: http://www.djangoproject.com/documentation/settings/#template-loaders .. _TEMPLATE_LOADERS: ../settings/#template-loaders
.. _sites framework: http://www.djangoproject.com/documentation/sites/ .. _sites framework: ../sites/
Initialization Initialization
============== ==============
@ -68,7 +68,7 @@ The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class ``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
(e.g., ``BlogSitemap(some_var)``). (e.g., ``BlogSitemap(some_var)``).
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Sitemap classes Sitemap classes
=============== ===============
@ -217,8 +217,8 @@ defined for the current ``SITE_ID`` (see the `sites documentation`_) and
creates an entry in the sitemap. These entries include only the ``location`` creates an entry in the sitemap. These entries include only the ``location``
attribute -- not ``lastmod``, ``changefreq`` or ``priority``. attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/ .. _flatpages: ../flatpages/
.. _sites documentation: http://www.djangoproject.com/documentation/sites/ .. _sites documentation: ../sites/
``GenericSitemap`` ``GenericSitemap``
------------------ ------------------
@ -232,7 +232,7 @@ the ``lastmod`` attribute in the generated sitemap. You may also pass
``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap`` ``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
constructor to specify these attributes for all URLs. constructor to specify these attributes for all URLs.
.. _generic views: http://www.djangoproject.com/documentation/generic_views/ .. _generic views: ../generic_views/
Example Example
------- -------
@ -261,7 +261,7 @@ Here's an example of a URLconf_ using both::
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}) (r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
) )
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Creating a sitemap index Creating a sitemap index
======================== ========================

View File

@ -276,8 +276,8 @@ you want your admin site to have access to all objects (not just site-specific
ones), put ``objects = models.Manager()`` in your model, before you define ones), put ``objects = models.Manager()`` in your model, before you define
``CurrentSiteManager``. ``CurrentSiteManager``.
.. _manager: http://www.djangoproject.com/documentation/model_api/#managers .. _manager: ../model_api/#managers
.. _manager documentation: http://www.djangoproject.com/documentation/model_api/#managers .. _manager documentation: ../model_api/#managers
How Django uses the sites framework How Django uses the sites framework
=================================== ===================================
@ -316,7 +316,7 @@ Here's how Django uses the sites framework:
* The shortcut view (``django.views.defaults.shortcut``) uses the domain of * The shortcut view (``django.views.defaults.shortcut``) uses the domain of
the current ``Site`` object when calculating an object's URL. the current ``Site`` object when calculating an object's URL.
.. _redirects framework: http://www.djangoproject.com/documentation/redirects/ .. _redirects framework: ../redirects/
.. _flatpages framework: http://www.djangoproject.com/documentation/flatpages/ .. _flatpages framework: ../flatpages/
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/ .. _syndication framework: ../syndication/
.. _authentication framework: http://www.djangoproject.com/documentation/authentication/ .. _authentication framework: ../authentication/

View File

@ -24,7 +24,7 @@ production setting. Use this only for development.
For information on serving static files in an Apache production environment, For information on serving static files in an Apache production environment,
see the `Django mod_python documentation`_. see the `Django mod_python documentation`_.
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/#serving-media-files .. _Django mod_python documentation: ../modpython/#serving-media-files
How to do it How to do it
============ ============
@ -49,7 +49,7 @@ Examples:
* The file ``/path/bar.jpg`` will not be accessible, because it doesn't * The file ``/path/bar.jpg`` will not be accessible, because it doesn't
fall under the document root. fall under the document root.
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Directory listings Directory listings
================== ==================
@ -122,4 +122,4 @@ associated with the ``django.views.static.serve`` view. If not
Of course, the catch here is that you'll have to remember to set ``DEBUG=False`` Of course, the catch here is that you'll have to remember to set ``DEBUG=False``
in your production settings file. But you should be doing that anyway. in your production settings file. But you should be doing that anyway.
.. _DEBUG setting: http://www.djangoproject.com/documentation/settings/#debug .. _DEBUG setting: ../settings/#debug

View File

@ -26,7 +26,7 @@ to determine which feed to output.
To create a feed, just write a ``Feed`` class and point to it in your URLconf_. To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
Initialization Initialization
-------------- --------------
@ -72,8 +72,8 @@ The above example registers two feeds:
Once that's set up, you just need to define the ``Feed`` classes themselves. Once that's set up, you just need to define the ``Feed`` classes themselves.
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf: ../url_dispatch/
.. _settings file: http://www.djangoproject.com/documentation/settings/ .. _settings file: ../settings/
Feed classes Feed classes
------------ ------------
@ -156,8 +156,8 @@ put into those elements.
{{ obj.description }} {{ obj.description }}
.. _chicagocrime.org: http://www.chicagocrime.org/ .. _chicagocrime.org: http://www.chicagocrime.org/
.. _object-relational mapper: http://www.djangoproject.com/documentation/db_api/ .. _object-relational mapper: ../db_api/
.. _Django templates: http://www.djangoproject.com/documentation/templates/ .. _Django templates: ../templates/
A complex example A complex example
----------------- -----------------
@ -277,7 +277,7 @@ Feeds created by the syndication framework automatically include the
appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
comes directly from your `LANGUAGE_CODE setting`_. comes directly from your `LANGUAGE_CODE setting`_.
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code .. _LANGUAGE_CODE setting: ../settings/#language-code
URLs URLs
---- ----
@ -292,7 +292,7 @@ Atom feeds require a ``<link rel="self">`` that defines the feed's current
location. The syndication framework populates this automatically, using the location. The syndication framework populates this automatically, using the
domain of the current site according to the SITE_ID setting. domain of the current site according to the SITE_ID setting.
.. _SITE_ID setting: http://www.djangoproject.com/documentation/settings/#site-id .. _SITE_ID setting: ../settings/#site-id
Publishing Atom and RSS feeds in tandem Publishing Atom and RSS feeds in tandem
--------------------------------------- ---------------------------------------

View File

@ -792,7 +792,7 @@ Note that if you use ``{% ssi %}``, you'll need to define
See also: ``{% include %}``. See also: ``{% include %}``.
.. _ALLOWED_INCLUDE_ROOTS: http://www.djangoproject.com/documentation/settings/#allowed-include-roots .. _ALLOWED_INCLUDE_ROOTS: ../settings/#allowed-include-roots
templatetag templatetag
~~~~~~~~~~~ ~~~~~~~~~~~
@ -1218,7 +1218,7 @@ django.contrib.humanize
A set of Django template filters useful for adding a "human touch" to data. See A set of Django template filters useful for adding a "human touch" to data. See
the `humanize documentation`_. the `humanize documentation`_.
.. _humanize documentation: http://www.djangoproject.com/documentation/add_ons/#humanize .. _humanize documentation: ../add_ons/#humanize
django.contrib.markup django.contrib.markup
--------------------- ---------------------

View File

@ -11,7 +11,7 @@ If you're looking to use the Django template system as part of another
application -- i.e., without the rest of the framework -- make sure to read application -- i.e., without the rest of the framework -- make sure to read
the `configuration`_ section later in this document. the `configuration`_ section later in this document.
.. _`The Django template language: For template authors`: http://www.djangoproject.com/documentation/templates/ .. _`The Django template language: For template authors`: ../templates/
Basics Basics
====== ======
@ -327,8 +327,8 @@ Note::
Here's what each of the default processors does: Here's what each of the default processors does:
.. _HttpRequest object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects .. _HttpRequest object: ../request_response/#httprequest-objects
.. _TEMPLATE_CONTEXT_PROCESSORS setting: http://www.djangoproject.com/documentation/settings/#template-context-processors .. _TEMPLATE_CONTEXT_PROCESSORS setting: ../settings/#template-context-processors
django.core.context_processors.auth django.core.context_processors.auth
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -353,9 +353,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
permissions that the currently logged-in user has. See the `permissions permissions that the currently logged-in user has. See the `permissions
docs`_. docs`_.
.. _user authentication docs: http://www.djangoproject.com/documentation/authentication/#users .. _user authentication docs: ../authentication/#users
.. _message docs: http://www.djangoproject.com/documentation/authentication/#messages .. _message docs: ../authentication/#messages
.. _permissions docs: http://www.djangoproject.com/documentation/authentication/#permissions .. _permissions docs: ../authentication/#permissions
django.core.context_processors.debug django.core.context_processors.debug
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -383,9 +383,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
See the `internationalization docs`_ for more. See the `internationalization docs`_ for more.
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages .. _LANGUAGES setting: ../settings/#languages
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code .. _LANGUAGE_CODE setting: ../settings/#language-code
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/ .. _internationalization docs: ../i18n/
django.core.context_processors.request django.core.context_processors.request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -944,7 +944,7 @@ The ``takes_context`` parameter defaults to ``False``. When it's set to *True*,
the tag is passed the context object, as in this example. That's the only the tag is passed the context object, as in this example. That's the only
difference between this case and the previous ``inclusion_tag`` example. difference between this case and the previous ``inclusion_tag`` example.
.. _tutorials: http://www.djangoproject.com/documentation/tutorial1/#creating-models .. _tutorials: ../tutorial1/#creating-models
Setting a variable in the context Setting a variable in the context
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1115,5 +1115,5 @@ settings you wish to specify. You might want to consider setting at least
`settings documentation`_, and any setting starting with *TEMPLATE_* `settings documentation`_, and any setting starting with *TEMPLATE_*
is of obvious interest. is of obvious interest.
.. _settings file: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable .. _settings file: ../settings/#using-settings-without-the-django-settings-module-environment-variable
.. _settings documentation: http://www.djangoproject.com/documentation/settings/ .. _settings documentation: ../settings/

View File

@ -13,7 +13,7 @@ changed unexpectedly as a result of the refactor.
Testing a web application is a complex task, as there are many Testing a web application is a complex task, as there are many
components of a web application that must be validated and tested. To components of a web application that must be validated and tested. To
help you test your application, Django provides a test execution help you test your application, Django provides a test execution
framework, and range of utilities that can be used to stimulate and framework, and range of utilities that can be used to simulate and
inspect various facets of a web application. inspect various facets of a web application.
This testing framework is currently under development, and may change This testing framework is currently under development, and may change
@ -220,7 +220,7 @@ can be invoked on the ``Client`` instance.
will result in the evaluation of a GET request equivalent to:: will result in the evaluation of a GET request equivalent to::
http://yoursite.com/customers/details/?name='fred'&age=7 http://yoursite.com/customers/details/?name=fred&age=7
``post(path, data={})`` ``post(path, data={})``
Make a POST request on the provided ``path``. The key-value pairs in the Make a POST request on the provided ``path``. The key-value pairs in the
@ -244,11 +244,11 @@ can be invoked on the ``Client`` instance.
``login(path, username, password)`` ``login(path, username, password)``
In a production site, it is likely that some views will be protected with In a production site, it is likely that some views will be protected with
the @login_required URL provided by ``django.contrib.auth``. Interacting the @login_required decorator provided by ``django.contrib.auth``. Interacting
with a URL that has been login protected is a slightly complex operation, with a URL that has been login protected is a slightly complex operation,
so the Test Client provides a simple URL to automate the login process. A so the Test Client provides a simple method to automate the login process. A
call to ``login()`` stimulates the series of GET and POST calls required call to ``login()`` stimulates the series of GET and POST calls required
to log a user into a @login_required protected URL. to log a user into a @login_required protected view.
If login is possible, the final return value of ``login()`` is the response If login is possible, the final return value of ``login()`` is the response
that is generated by issuing a GET request on the protected URL. If login that is generated by issuing a GET request on the protected URL. If login
@ -415,7 +415,7 @@ arguments:
tested. This is the same format returned by ``django.db.models.get_apps()`` tested. This is the same format returned by ``django.db.models.get_apps()``
Verbosity determines the amount of notification and debug information that Verbosity determines the amount of notification and debug information that
will be printed to the console; '0' is no output, '1' is normal output, will be printed to the console; `0` is no output, `1` is normal output,
and `2` is verbose output. and `2` is verbose output.
Testing utilities Testing utilities

View File

@ -17,7 +17,7 @@ installed by running the Python interactive interpreter and typing
``import django``. If that command runs successfully, with no errors, Django is ``import django``. If that command runs successfully, with no errors, Django is
installed. installed.
.. _`Django installed`: http://www.djangoproject.com/documentation/install/ .. _`Django installed`: ../install/
Creating a project Creating a project
================== ==================
@ -108,7 +108,7 @@ It worked!
Full docs for the development server are at `django-admin documentation`_. Full docs for the development server are at `django-admin documentation`_.
.. _django-admin documentation: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin documentation: ../django_admin/
Database setup Database setup
-------------- --------------
@ -378,7 +378,7 @@ as you like, and it will only ever create the tables that don't exist.
Read the `django-admin.py documentation`_ for full information on what the Read the `django-admin.py documentation`_ for full information on what the
``manage.py`` utility can do. ``manage.py`` utility can do.
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/ .. _django-admin.py documentation: ../django_admin/
Playing with the API Playing with the API
==================== ====================
@ -555,5 +555,5 @@ For full details on the database API, see our `Database API reference`_.
When you're comfortable with the API, read `part 2 of this tutorial`_ to get When you're comfortable with the API, read `part 2 of this tutorial`_ to get
Django's automatic admin working. Django's automatic admin working.
.. _Database API reference: http://www.djangoproject.com/documentation/db_api/ .. _Database API reference: ../db_api/
.. _part 2 of this tutorial: http://www.djangoproject.com/documentation/tutorial2/ .. _part 2 of this tutorial: ../tutorial2/

View File

@ -5,7 +5,7 @@ Writing your first Django app, part 2
This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll
application and will focus on Django's automatically-generated admin site. application and will focus on Django's automatically-generated admin site.
.. _Tutorial 1: http://www.djangoproject.com/documentation/tutorial1/ .. _Tutorial 1: ../tutorial1/
.. admonition:: Philosophy .. admonition:: Philosophy
@ -64,7 +64,7 @@ tutorial, remember?) You should see the Django admin index page:
By default, you should see two types of editable content: groups and users. By default, you should see two types of editable content: groups and users.
These are core features Django ships with by default. These are core features Django ships with by default.
.. _"I can't log in" questions: http://www.djangoproject.com/documentation/faq/#the-admin-site .. _"I can't log in" questions: ../faq/#the-admin-site
Make the poll app modifiable in the admin Make the poll app modifiable in the admin
========================================= =========================================
@ -402,7 +402,7 @@ Django automatically looks for a ``templates/`` subdirectory within each app
package, for use as a fallback. See the `loader types documentation`_ for full package, for use as a fallback. See the `loader types documentation`_ for full
information. information.
.. _loader types documentation: http://www.djangoproject.com/documentation/templates_python/#loader-types .. _loader types documentation: ../templates_python/#loader-types
Customize the admin index page Customize the admin index page
============================== ==============================
@ -433,5 +433,5 @@ general, see the `Django admin CSS guide`_.
When you're comfortable with the admin site, read `part 3 of this tutorial`_ to When you're comfortable with the admin site, read `part 3 of this tutorial`_ to
start working on public poll views. start working on public poll views.
.. _Django admin CSS guide: http://www.djangoproject.com/documentation/admin_css/ .. _Django admin CSS guide: ../admin_css/
.. _part 3 of this tutorial: http://www.djangoproject.com/documentation/tutorial3/ .. _part 3 of this tutorial: ../tutorial3/

View File

@ -5,7 +5,7 @@ Writing your first Django app, part 3
This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
application and will focus on creating the public interface -- "views." application and will focus on creating the public interface -- "views."
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/ .. _Tutorial 2: ../tutorial2/
Philosophy Philosophy
========== ==========
@ -117,8 +117,8 @@ time the URLconf module is loaded. They're super fast.
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression .. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
.. _Python documentation: http://www.python.org/doc/current/lib/module-re.html .. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
.. _request and response documentation: http://www.djangoproject.com/documentation/request_response/ .. _request and response documentation: ../request_response/
.. _URLconf documentation: http://www.djangoproject.com/documentation/url_dispatch/ .. _URLconf documentation: ../url_dispatch/
Write your first view Write your first view
===================== =====================
@ -260,8 +260,7 @@ provides a shortcut. Here's the full ``index()`` view, rewritten::
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5] latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list}) return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
Note that we no longer need to import ``loader``, ``Context`` or Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
``HttpResponse``.
The ``render_to_response()`` function takes a template name as its first The ``render_to_response()`` function takes a template name as its first
argument and a dictionary as its optional second argument. It returns an argument and a dictionary as its optional second argument. It returns an
@ -377,7 +376,7 @@ iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
See the `template guide`_ for full details on how templates work. See the `template guide`_ for full details on how templates work.
.. _template guide: http://www.djangoproject.com/documentation/templates/ .. _template guide: ../templates/
Simplifying the URLconfs Simplifying the URLconfs
======================== ========================
@ -464,4 +463,4 @@ All the poll app cares about is its relative URLs, not its absolute URLs.
When you're comfortable with writing views, read `part 4 of this tutorial`_ to When you're comfortable with writing views, read `part 4 of this tutorial`_ to
learn about simple form processing and generic views. learn about simple form processing and generic views.
.. _part 4 of this tutorial: http://www.djangoproject.com/documentation/tutorial4/ .. _part 4 of this tutorial: ../tutorial4/

View File

@ -120,7 +120,7 @@ Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a
results page that gets updated each time you vote. If you submit the form results page that gets updated each time you vote. If you submit the form
without having chosen a choice, you should see the error message. without having chosen a choice, you should see the error message.
.. _request and response documentation: http://www.djangoproject.com/documentation/request_response/ .. _request and response documentation: ../request_response/
Use generic views: Less code is better Use generic views: Less code is better
====================================== ======================================
@ -226,7 +226,7 @@ Run the server, and use your new polling app based on generic views.
For full details on generic views, see the `generic views documentation`_. For full details on generic views, see the `generic views documentation`_.
.. _generic views documentation: http://www.djangoproject.com/documentation/generic_views/ .. _generic views documentation: ../generic_views/
Coming soon Coming soon
=========== ===========
@ -241,4 +241,4 @@ installments:
* Advanced admin features: Permissions * Advanced admin features: Permissions
* Advanced admin features: Custom JavaScript * Advanced admin features: Custom JavaScript
.. _Tutorial 3: http://www.djangoproject.com/documentation/tutorial3/ .. _Tutorial 3: ../tutorial3/

View File

@ -45,8 +45,8 @@ algorithm the system follows to determine which Python code to execute:
`request object`_ as its first argument and any values captured in the `request object`_ as its first argument and any values captured in the
regex as remaining arguments. regex as remaining arguments.
.. _settings file: http://www.djangoproject.com/documentation/settings/ .. _settings file: ../settings/
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects .. _request object: ../request_response/#httprequest-objects
Example Example
======= =======
@ -286,7 +286,7 @@ With this in mind, the above example can be written more concisely as::
Note that you don't put a trailing dot (``"."``) in the prefix. Django puts Note that you don't put a trailing dot (``"."``) in the prefix. Django puts
that in automatically. that in automatically.
.. _Django overview: http://www.djangoproject.com/documentation/overview/ .. _Django overview: ../overview/
Multiple view prefixes Multiple view prefixes
---------------------- ----------------------
@ -387,13 +387,13 @@ In this example, for a request to ``/blog/2005/``, Django will call the
This technique is used in `generic views`_ and in the `syndication framework`_ This technique is used in `generic views`_ and in the `syndication framework`_
to pass metadata and options to views. to pass metadata and options to views.
.. _generic views: http://www.djangoproject.com/documentation/generic_views/ .. _generic views: ../generic_views/
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/ .. _syndication framework: ../syndication/
Passing extra options to ``include()`` Passing extra options to ``include()``
-------------------------------------- --------------------------------------
**New in the Django development version.** **New in Django development version.**
Similarly, you can pass extra options to ``include()``. When you pass extra Similarly, you can pass extra options to ``include()``. When you pass extra
options to ``include()``, *each* line in the included URLconf will be passed options to ``include()``, *each* line in the included URLconf will be passed
@ -435,7 +435,7 @@ every view in the the included URLconf accepts the extra options you're passing.
Passing callable objects instead of strings Passing callable objects instead of strings
=========================================== ===========================================
**New in the Django development version.** **New in Django development version.**
Some developers find it more natural to pass the actual Python function object Some developers find it more natural to pass the actual Python function object
rather than a string containing the path to its module. This alternative is rather than a string containing the path to its module. This alternative is

View File

@ -1,53 +1,105 @@
""" """
17. Custom column names 17. Custom column/table names
If your database column name is different than your model attribute, use the If your database column name is different than your model attribute, use the
``db_column`` parameter. Note that you'll use the field's name, not its column ``db_column`` parameter. Note that you'll use the field's name, not its column
name, in API usage. name, in API usage.
If your database table name is different than your model name, use the
``db_table`` Meta attribute. This has no effect on the API used to
query the database.
If you need to use a table name for a many-to-many relationship that differs
from the default generated name, use the ``db_table`` parameter on the
ManyToMany field. This has no effect on the API for querying the database.
""" """
from django.db import models from django.db import models
class Person(models.Model): class Author(models.Model):
first_name = models.CharField(maxlength=30, db_column='firstname') first_name = models.CharField(maxlength=30, db_column='firstname')
last_name = models.CharField(maxlength=30, db_column='last') last_name = models.CharField(maxlength=30, db_column='last')
def __str__(self): def __str__(self):
return '%s %s' % (self.first_name, self.last_name) return '%s %s' % (self.first_name, self.last_name)
__test__ = {'API_TESTS':""" class Meta:
# Create a Person. db_table = 'my_author_table'
>>> p = Person(first_name='John', last_name='Smith') ordering = ('last_name','first_name')
>>> p.save()
>>> p.id class Article(models.Model):
headline = models.CharField(maxlength=100)
authors = models.ManyToManyField(Author, db_table='my_m2m_table')
def __str__(self):
return self.headline
class Meta:
ordering = ('headline',)
__test__ = {'API_TESTS':"""
# Create a Author.
>>> a = Author(first_name='John', last_name='Smith')
>>> a.save()
>>> a.id
1 1
>>> Person.objects.all() # Create another author
[<Person: John Smith>] >>> a2 = Author(first_name='Peter', last_name='Jones')
>>> a2.save()
>>> Person.objects.filter(first_name__exact='John') # Create an article
[<Person: John Smith>] >>> art = Article(headline='Django lets you build web apps easily')
>>> art.save()
>>> art.authors = [a, a2]
>>> Person.objects.get(first_name__exact='John') # Although the table and column names on Author have been set to
<Person: John Smith> # custom values, nothing about using the Author model has changed...
>>> Person.objects.filter(firstname__exact='John') # Query the available authors
>>> Author.objects.all()
[<Author: Peter Jones>, <Author: John Smith>]
>>> Author.objects.filter(first_name__exact='John')
[<Author: John Smith>]
>>> Author.objects.get(first_name__exact='John')
<Author: John Smith>
>>> Author.objects.filter(firstname__exact='John')
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: Cannot resolve keyword 'firstname' into field TypeError: Cannot resolve keyword 'firstname' into field
>>> p = Person.objects.get(last_name__exact='Smith') >>> a = Author.objects.get(last_name__exact='Smith')
>>> p.first_name >>> a.first_name
'John' 'John'
>>> p.last_name >>> a.last_name
'Smith' 'Smith'
>>> p.firstname >>> a.firstname
Traceback (most recent call last): Traceback (most recent call last):
... ...
AttributeError: 'Person' object has no attribute 'firstname' AttributeError: 'Author' object has no attribute 'firstname'
>>> p.last >>> a.last
Traceback (most recent call last): Traceback (most recent call last):
... ...
AttributeError: 'Person' object has no attribute 'last' AttributeError: 'Author' object has no attribute 'last'
# Although the Article table uses a custom m2m table,
# nothing about using the m2m relationship has changed...
# Get all the authors for an article
>>> art.authors.all()
[<Author: Peter Jones>, <Author: John Smith>]
# Get the articles for an author
>>> a.article_set.all()
[<Article: Django lets you build web apps easily>]
# Query the authors across the m2m relation
>>> art.authors.filter(last_name='Jones')
[<Author: Peter Jones>]
"""} """}

View File

@ -65,14 +65,14 @@ __test__ = {'API_TESTS':"""
# Objects with declared GenericRelations can be tagged directly -- the API # Objects with declared GenericRelations can be tagged directly -- the API
# mimics the many-to-many API. # mimics the many-to-many API.
>>> lion.tags.create(tag="yellow")
<TaggedItem: yellow>
>>> lion.tags.create(tag="hairy")
<TaggedItem: hairy>
>>> bacon.tags.create(tag="fatty") >>> bacon.tags.create(tag="fatty")
<TaggedItem: fatty> <TaggedItem: fatty>
>>> bacon.tags.create(tag="salty") >>> bacon.tags.create(tag="salty")
<TaggedItem: salty> <TaggedItem: salty>
>>> lion.tags.create(tag="yellow")
<TaggedItem: yellow>
>>> lion.tags.create(tag="hairy")
<TaggedItem: hairy>
>>> lion.tags.all() >>> lion.tags.all()
[<TaggedItem: hairy>, <TaggedItem: yellow>] [<TaggedItem: hairy>, <TaggedItem: yellow>]
@ -105,4 +105,30 @@ __test__ = {'API_TESTS':"""
[<TaggedItem: shiny>] [<TaggedItem: shiny>]
>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id) >>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
[<TaggedItem: clearish>] [<TaggedItem: clearish>]
# If you delete an object with an explicit Generic relation, the related
# objects are deleted when the source object is deleted.
# Original list of tags:
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('hairy', <ContentType: animal>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2), ('yellow', <ContentType: animal>, 1)]
>>> lion.delete()
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
# If Generic Relation is not explicitly defined, any related objects
# remain after deletion of the source object.
>>> quartz.delete()
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
# If you delete a tag, the objects using the tag are unaffected
# (other than losing a tag)
>>> tag = TaggedItem.objects.get(id=1)
>>> tag.delete()
>>> bacon.tags.all()
[<TaggedItem: salty>]
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[('clearish', <ContentType: mineral>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
"""} """}

View File

@ -191,4 +191,19 @@ DoesNotExist: Article matching query does not exist.
>>> Article.objects.filter(headline__contains='\\') >>> Article.objects.filter(headline__contains='\\')
[<Article: Article with \ backslash>] [<Article: Article with \ backslash>]
# none() returns an EmptyQuerySet that behaves like any other QuerySet object
>>> Article.objects.none()
[]
>>> Article.objects.none().filter(headline__startswith='Article')
[]
>>> Article.objects.none().count()
0
# using __in with an empty list should return an empty query set
>>> Article.objects.filter(id__in=[])
[]
>>> Article.objects.exclude(id__in=[])
[<Article: Article with \ backslash>, <Article: Article% with percent sign>, <Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
"""} """}

View File

@ -203,7 +203,19 @@ __test__ = {'API_TESTS':"""
>>> p2.article_set.all() >>> p2.article_set.all()
[<Article: Oxygen-free diet works wonders>] [<Article: Oxygen-free diet works wonders>]
# Recreate the article and Publication we just deleted. # Relation sets can also be set using primary key values
>>> p2.article_set = [a4.id, a5.id]
>>> p2.article_set.all()
[<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]
>>> a4.publications.all()
[<Publication: Science News>]
>>> a4.publications = [p3.id]
>>> p2.article_set.all()
[<Article: Oxygen-free diet works wonders>]
>>> a4.publications.all()
[<Publication: Science Weekly>]
# Recreate the article and Publication we have deleted.
>>> p1 = Publication(id=None, title='The Python Journal') >>> p1 = Publication(id=None, title='The Python Journal')
>>> p1.save() >>> p1.save()
>>> a2 = Article(id=None, headline='NASA uses Python') >>> a2 = Article(id=None, headline='NASA uses Python')

View File

@ -6,17 +6,20 @@ model instance.
The function django.newforms.form_for_model() takes a model class and returns 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, 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 with one additional method: save(). The save() method creates an instance
of the model and returns that newly created instance. It saves the instance to 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 the database if save(commit=True), which is default. If you pass
create(save=False), then you'll get the object without saving it. commit=False, then you'll get the object without committing the changes to the
database.
The function django.newforms.form_for_instance() takes a model instance and The function django.newforms.form_for_instance() takes a model instance and
returns a Form that is tied to the instance. This form works just like any returns a Form that is tied to the instance. This form works just like any
other Form, with one additional method: apply_changes(). The apply_changes() other Form, with one additional method: save(). The save()
method updates the model instance. It saves the changes to the database if method updates the model instance. It also takes a commit=True parameter.
apply_changes(save=True), which is default. If you pass save=False, then you'll
get the object without saving it. The function django.newforms.save_instance() takes a bound form instance and a
model instance and saves the form's clean_data into the instance. It also takes
a commit=True parameter.
""" """
from django.db import models from django.db import models
@ -29,7 +32,7 @@ class Category(models.Model):
return self.name return self.name
class Writer(models.Model): class Writer(models.Model):
name = models.CharField(maxlength=50) name = models.CharField(maxlength=50, help_text='Use both first and last names.')
def __str__(self): def __str__(self):
return self.name return self.name
@ -38,13 +41,14 @@ class Article(models.Model):
headline = models.CharField(maxlength=50) headline = models.CharField(maxlength=50)
pub_date = models.DateField() pub_date = models.DateField()
writer = models.ForeignKey(Writer) writer = models.ForeignKey(Writer)
article = models.TextField()
categories = models.ManyToManyField(Category, blank=True) categories = models.ManyToManyField(Category, blank=True)
def __str__(self): def __str__(self):
return self.headline return self.headline
__test__ = {'API_TESTS': """ __test__ = {'API_TESTS': """
>>> from django.newforms import form_for_model, form_for_instance, BaseForm >>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
>>> import datetime >>> import datetime
>>> Category.objects.all() >>> Category.objects.all()
@ -67,35 +71,36 @@ __test__ = {'API_TESTS': """
<li>The URL: <input type="text" name="url" maxlength="40" /></li> <li>The URL: <input type="text" name="url" maxlength="40" /></li>
>>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'}) >>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
>>> f.errors >>> f.is_valid()
{} True
>>> f.clean_data >>> f.clean_data
{'url': u'entertainment', 'name': u'Entertainment'} {'url': u'entertainment', 'name': u'Entertainment'}
>>> obj = f.create() >>> obj = f.save()
>>> obj >>> obj
<Category: Entertainment> <Category: Entertainment>
>>> Category.objects.all() >>> Category.objects.all()
[<Category: Entertainment>] [<Category: Entertainment>]
>>> f = CategoryForm({'name': "It's a test", 'url': 'test'}) >>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
>>> f.errors >>> f.is_valid()
{} True
>>> f.clean_data >>> f.clean_data
{'url': u'test', 'name': u"It's a test"} {'url': u'test', 'name': u"It's a test"}
>>> obj = f.create() >>> obj = f.save()
>>> obj >>> obj
<Category: It's a test> <Category: It's a test>
>>> Category.objects.all() >>> Category.objects.all()
[<Category: Entertainment>, <Category: It's a test>] [<Category: Entertainment>, <Category: It's a test>]
If you call create() with save=False, then it will return an object that hasn't If you call save() with commit=False, then it will return an object that
yet been saved. In this case, it's up to you to save it. hasn't yet been saved to the database. In this case, it's up to you to call
save() on the resulting model instance.
>>> f = CategoryForm({'name': 'Third test', 'url': 'third'}) >>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
>>> f.errors >>> f.is_valid()
{} True
>>> f.clean_data >>> f.clean_data
{'url': u'third', 'name': u'Third test'} {'url': u'third', 'name': u'Third test'}
>>> obj = f.create(save=False) >>> obj = f.save(commit=False)
>>> obj >>> obj
<Category: Third test> <Category: Third test>
>>> Category.objects.all() >>> Category.objects.all()
@ -104,17 +109,20 @@ yet been saved. In this case, it's up to you to save it.
>>> Category.objects.all() >>> Category.objects.all()
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>] [<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
If you call create() with invalid data, you'll get a ValueError. If you call save() with invalid data, you'll get a ValueError.
>>> f = CategoryForm({'name': '', 'url': 'foo'}) >>> f = CategoryForm({'name': '', 'url': 'foo'})
>>> f.errors >>> f.errors
{'name': [u'This field is required.']} {'name': [u'This field is required.']}
>>> f.clean_data >>> f.clean_data
>>> f.create() Traceback (most recent call last):
...
AttributeError: 'CategoryForm' object has no attribute 'clean_data'
>>> f.save()
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: The Category could not be created because the data didn't validate. ValueError: The Category could not be created because the data didn't validate.
>>> f = CategoryForm({'name': '', 'url': 'foo'}) >>> f = CategoryForm({'name': '', 'url': 'foo'})
>>> f.create() >>> f.save()
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: The Category could not be created because the data didn't validate. ValueError: The Category could not be created because the data didn't validate.
@ -137,11 +145,12 @@ represented by a ChoiceField.
<option value="1">Mike Royko</option> <option value="1">Mike Royko</option>
<option value="2">Bob Woodward</option> <option value="2">Bob Woodward</option>
</select></td></tr> </select></td></tr>
<tr><th>Article:</th><td><textarea name="article"></textarea></td></tr>
<tr><th>Categories:</th><td><select multiple="multiple" name="categories"> <tr><th>Categories:</th><td><select multiple="multiple" name="categories">
<option value="1">Entertainment</option> <option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option> <option value="2">It&#39;s a test</option>
<option value="3">Third test</option> <option value="3">Third test</option>
</select></td></tr> </select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
You can pass a custom Form class to form_for_model. Make sure it's a You can pass a custom Form class to form_for_model. Make sure it's a
subclass of BaseForm, not Form. subclass of BaseForm, not Form.
@ -153,17 +162,16 @@ subclass of BaseForm, not Form.
>>> f.say_hello() >>> f.say_hello()
hello hello
Use form_for_instance to create a Form from a model instance. There are two Use form_for_instance to create a Form from a model instance. The difference
differences between this Form and one created via form_for_model. First, the between this Form and one created via form_for_model is that the object's
object's current values are inserted as 'initial' data in each Field. Second, current values are inserted as 'initial' data in each Field.
the Form gets an apply_changes() method instead of a create() method.
>>> w = Writer.objects.get(name='Mike Royko') >>> w = Writer.objects.get(name='Mike Royko')
>>> RoykoForm = form_for_instance(w) >>> RoykoForm = form_for_instance(w)
>>> f = RoykoForm(auto_id=False) >>> f = RoykoForm(auto_id=False)
>>> print f >>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /></td></tr> <tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
>>> art = Article(headline='Test article', pub_date=datetime.date(1988, 1, 4), writer=w) >>> art = Article(headline='Test article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.')
>>> art.save() >>> art.save()
>>> art.id >>> art.id
1 1
@ -177,15 +185,16 @@ the Form gets an apply_changes() method instead of a create() method.
<option value="1" selected="selected">Mike Royko</option> <option value="1" selected="selected">Mike Royko</option>
<option value="2">Bob Woodward</option> <option value="2">Bob Woodward</option>
</select></li> </select></li>
<li>Article: <textarea name="article">Hello.</textarea></li>
<li>Categories: <select multiple="multiple" name="categories"> <li>Categories: <select multiple="multiple" name="categories">
<option value="1">Entertainment</option> <option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option> <option value="2">It&#39;s a test</option>
<option value="3">Third test</option> <option value="3">Third test</option>
</select></li> </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1'}) >>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1', 'article': 'Hello.'})
>>> f.is_valid() >>> f.is_valid()
True True
>>> new_art = f.apply_changes() >>> new_art = f.save()
>>> new_art.id >>> new_art.id
1 1
>>> new_art = Article.objects.get(id=1) >>> new_art = Article.objects.get(id=1)
@ -208,10 +217,68 @@ Add some categories and test the many-to-many form output.
<option value="1" selected="selected">Mike Royko</option> <option value="1" selected="selected">Mike Royko</option>
<option value="2">Bob Woodward</option> <option value="2">Bob Woodward</option>
</select></li> </select></li>
<li>Article: <textarea name="article">Hello.</textarea></li>
<li>Categories: <select multiple="multiple" name="categories"> <li>Categories: <select multiple="multiple" name="categories">
<option value="1" selected="selected">Entertainment</option> <option value="1" selected="selected">Entertainment</option>
<option value="2">It&#39;s a test</option> <option value="2">It&#39;s a test</option>
<option value="3">Third test</option> <option value="3">Third test</option>
</select></li> </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04',
... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']})
>>> new_art = f.save()
>>> new_art.id
1
>>> new_art = Article.objects.get(id=1)
>>> new_art.categories.all()
[<Category: Entertainment>, <Category: It's a test>]
Now, submit form data with no categories. This deletes the existing categories.
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04',
... 'writer': u'1', 'article': u'Hello.'})
>>> new_art = f.save()
>>> new_art.id
1
>>> new_art = Article.objects.get(id=1)
>>> new_art.categories.all()
[]
Create a new article, with categories, via the form.
>>> ArticleForm = form_for_model(Article)
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'pub_date': u'1967-11-01',
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
>>> new_art = f.save()
>>> new_art.id
2
>>> new_art = Article.objects.get(id=2)
>>> new_art.categories.all()
[<Category: Entertainment>, <Category: It's a test>]
Create a new article, with no categories, via the form.
>>> ArticleForm = form_for_model(Article)
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'pub_date': u'1967-11-01',
... 'writer': u'1', 'article': u'Test.'})
>>> new_art = f.save()
>>> new_art.id
3
>>> new_art = Article.objects.get(id=3)
>>> new_art.categories.all()
[]
Here, we define a custom Form. Because it happens to have the same fields as
the Category model, we can use save_instance() to apply its changes to an
existing Category instance.
>>> class ShortCategory(Form):
... name = CharField(max_length=5)
... url = CharField(max_length=3)
>>> cat = Category.objects.get(name='Third test')
>>> cat
<Category: Third test>
>>> cat.id
3
>>> sc = ShortCategory({'name': 'Third', 'url': '3rd'})
>>> save_instance(sc, cat)
<Category: Third>
>>> Category.objects.get(id=3)
<Category: Third>
"""} """}

View File

@ -69,6 +69,21 @@ __test__ = {'API_TESTS':"""
>>> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3)) >>> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>] [<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
# You could also use "in" to accomplish the same as above.
>>> Article.objects.filter(pk__in=[1,2,3])
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
>>> Article.objects.filter(pk__in=[1,2,3,4])
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
# Passing "in" an empty list returns no results ...
>>> Article.objects.filter(pk__in=[])
[]
# ... but can return results if we OR it with another query.
>>> Article.objects.filter(Q(pk__in=[]) | Q(headline__icontains='goodbye'))
[<Article: Goodbye>, <Article: Hello and goodbye>]
# Q arg objects are ANDed # Q arg objects are ANDed
>>> Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye')) >>> Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye'))
[<Article: Hello and goodbye>] [<Article: Hello and goodbye>]

View File

@ -37,6 +37,13 @@ class Article(models.Model):
def __str__(self): def __str__(self):
return self.headline return self.headline
class AuthorProfile(models.Model):
author = models.OneToOneField(Author)
date_of_birth = models.DateField()
def __str__(self):
return "Profile of %s" % self.author
__test__ = {'API_TESTS':""" __test__ = {'API_TESTS':"""
# Create some data: # Create some data:
>>> from datetime import datetime >>> from datetime import datetime
@ -118,4 +125,18 @@ __test__ = {'API_TESTS':"""
>>> Article.objects.all() >>> Article.objects.all()
[<Article: Just kidding; I love TV poker>, <Article: Time to reform copyright>] [<Article: Just kidding; I love TV poker>, <Article: Time to reform copyright>]
# If you use your own primary key field (such as a OneToOneField),
# it doesn't appear in the serialized field list - it replaces the
# pk identifier.
>>> profile = AuthorProfile(author=joe, date_of_birth=datetime(1970,1,1))
>>> profile.save()
>>> json = serializers.serialize("json", AuthorProfile.objects.all())
>>> json
'[{"pk": "1", "model": "serializers.authorprofile", "fields": {"date_of_birth": "1970-01-01"}}]'
>>> for obj in serializers.deserialize("json", json):
... print obj
<DeserializedObject: Profile of Joe>
"""} """}

View File

@ -26,10 +26,10 @@ def redirect_view(request):
"A view that redirects all requests to the GET view" "A view that redirects all requests to the GET view"
return HttpResponseRedirect('/test_client/get_view/') return HttpResponseRedirect('/test_client/get_view/')
@login_required
def login_protected_view(request): def login_protected_view(request):
"A simple view that is login protected." "A simple view that is login protected."
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template') t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
c = Context({'user': request.user}) c = Context({'user': request.user})
return HttpResponse(t.render(c)) return HttpResponse(t.render(c))
login_protected_view = login_required(login_protected_view)

View File

@ -106,6 +106,46 @@ u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u
>>> w.render('email', '', attrs={'class': 'special'}) >>> w.render('email', '', attrs={'class': 'special'})
u'<input type="hidden" class="special" name="email" />' u'<input type="hidden" class="special" name="email" />'
# MultipleHiddenInput Widget ##################################################
>>> w = MultipleHiddenInput()
>>> w.render('email', [])
u''
>>> w.render('email', None)
u''
>>> w.render('email', ['test@example.com'])
u'<input type="hidden" name="email" value="test@example.com" />'
>>> w.render('email', ['some "quoted" & ampersanded value'])
u'<input type="hidden" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
>>> w.render('email', ['test@example.com', 'foo@example.com'])
u'<input type="hidden" name="email" value="test@example.com" />\n<input type="hidden" name="email" value="foo@example.com" />'
>>> w.render('email', ['test@example.com'], attrs={'class': 'fun'})
u'<input type="hidden" name="email" value="test@example.com" class="fun" />'
>>> w.render('email', ['test@example.com', 'foo@example.com'], attrs={'class': 'fun'})
u'<input type="hidden" name="email" value="test@example.com" class="fun" />\n<input type="hidden" name="email" value="foo@example.com" class="fun" />'
You can also pass 'attrs' to the constructor:
>>> w = MultipleHiddenInput(attrs={'class': 'fun'})
>>> w.render('email', [])
u''
>>> w.render('email', ['foo@example.com'])
u'<input type="hidden" class="fun" value="foo@example.com" name="email" />'
>>> w.render('email', ['foo@example.com', 'test@example.com'])
u'<input type="hidden" class="fun" value="foo@example.com" name="email" />\n<input type="hidden" class="fun" value="test@example.com" name="email" />'
'attrs' passed to render() get precedence over those passed to the constructor:
>>> w = MultipleHiddenInput(attrs={'class': 'pretty'})
>>> w.render('email', ['foo@example.com'], attrs={'class': 'special'})
u'<input type="hidden" class="special" value="foo@example.com" name="email" />'
>>> w.render('email', ['ŠĐĆŽćžšđ'], attrs={'class': 'fun'})
u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
'attrs' passed to render() get precedence over those passed to the constructor:
>>> w = MultipleHiddenInput(attrs={'class': 'pretty'})
>>> w.render('email', ['foo@example.com'], attrs={'class': 'special'})
u'<input type="hidden" class="special" value="foo@example.com" name="email" />'
# FileInput Widget ############################################################ # FileInput Widget ############################################################
>>> w = FileInput() >>> w = FileInput()
@ -296,6 +336,60 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
>>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) >>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>' u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
If choices is passed to the constructor and is a generator, it can be iterated
over multiple times without getting consumed:
>>> w = Select(choices=get_choices())
>>> print w.render('num', 2)
<select name="num">
<option value="0">0</option>
<option value="1">1</option>
<option value="2" selected="selected">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
>>> print w.render('num', 3)
<select name="num">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3" selected="selected">3</option>
<option value="4">4</option>
</select>
# NullBooleanSelect Widget ####################################################
>>> w = NullBooleanSelect()
>>> print w.render('is_cool', True)
<select name="is_cool">
<option value="1">Unknown</option>
<option value="2" selected="selected">Yes</option>
<option value="3">No</option>
</select>
>>> print w.render('is_cool', False)
<select name="is_cool">
<option value="1">Unknown</option>
<option value="2">Yes</option>
<option value="3" selected="selected">No</option>
</select>
>>> print w.render('is_cool', None)
<select name="is_cool">
<option value="1" selected="selected">Unknown</option>
<option value="2">Yes</option>
<option value="3">No</option>
</select>
>>> print w.render('is_cool', '2')
<select name="is_cool">
<option value="1">Unknown</option>
<option value="2" selected="selected">Yes</option>
<option value="3">No</option>
</select>
>>> print w.render('is_cool', '3')
<select name="is_cool">
<option value="1">Unknown</option>
<option value="2">Yes</option>
<option value="3" selected="selected">No</option>
</select>
# SelectMultiple Widget ####################################################### # SelectMultiple Widget #######################################################
>>> w = SelectMultiple() >>> w = SelectMultiple()
@ -527,12 +621,16 @@ True
>>> r[1].is_checked() >>> r[1].is_checked()
False False
>>> r[1].name, r[1].value, r[1].choice_value, r[1].choice_label >>> r[1].name, r[1].value, r[1].choice_value, r[1].choice_label
('beatle', u'J', 'P', 'Paul') ('beatle', u'J', u'P', u'Paul')
>>> r[10] >>> r[10]
Traceback (most recent call last): Traceback (most recent call last):
... ...
IndexError: list index out of range IndexError: list index out of range
>>> w = RadioSelect()
>>> unicode(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]))
u'<ul>\n<li><label><input checked="checked" type="radio" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="radio" name="email" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>'
# CheckboxSelectMultiple Widget ############################################### # CheckboxSelectMultiple Widget ###############################################
>>> w = CheckboxSelectMultiple() >>> w = CheckboxSelectMultiple()
@ -640,6 +738,39 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) >>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
u'<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>\n<li><label><input type="checkbox" name="nums" value="2" /> 2</label></li>\n<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>\n<li><label><input checked="checked" type="checkbox" name="nums" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="checkbox" name="nums" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>' u'<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>\n<li><label><input type="checkbox" name="nums" value="2" /> 2</label></li>\n<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>\n<li><label><input checked="checked" type="checkbox" name="nums" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="checkbox" name="nums" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>'
# MultiWidget #################################################################
>>> class MyMultiWidget(MultiWidget):
... def decompress(self, value):
... if value:
... return value.split('__')
... return ['', '']
... def format_output(self, rendered_widgets):
... return u'<br />'.join(rendered_widgets)
>>> w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})))
>>> w.render('name', ['john', 'lennon'])
u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />'
>>> w.render('name', 'john__lennon')
u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />'
# SplitDateTimeWidget #########################################################
>>> w = SplitDateTimeWidget()
>>> w.render('date', '')
u'<input type="text" name="date_0" /><input type="text" name="date_1" />'
>>> w.render('date', None)
u'<input type="text" name="date_0" /><input type="text" name="date_1" />'
>>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30))
u'<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />'
>>> w.render('date', [datetime.date(2006, 1, 10), datetime.time(7, 30)])
u'<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />'
You can also pass 'attrs' to the constructor. In this case, the attrs will be
included on both widgets.
>>> w = SplitDateTimeWidget(attrs={'class': 'pretty'})
>>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30))
u'<input type="text" class="pretty" value="2006-01-10" name="date_0" /><input type="text" class="pretty" value="07:30:00" name="date_1" />'
########## ##########
# Fields # # Fields #
########## ##########
@ -766,9 +897,11 @@ ValidationError: [u'Enter a whole number.']
>>> f = IntegerField(required=False) >>> f = IntegerField(required=False)
>>> f.clean('') >>> f.clean('')
u'' >>> repr(f.clean(''))
'None'
>>> f.clean(None) >>> f.clean(None)
u'' >>> repr(f.clean(None))
'None'
>>> f.clean('1') >>> f.clean('1')
1 1
>>> isinstance(f.clean('1'), int) >>> isinstance(f.clean('1'), int)
@ -1285,6 +1418,11 @@ ValidationError: [u'This URL appears to be a broken link.']
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValidationError: [u'This URL appears to be a broken link.'] ValidationError: [u'This URL appears to be a broken link.']
>>> f = URLField(verify_exists=True, required=False)
>>> f.clean('')
u''
>>> f.clean('http://www.google.com') # This will fail if there's no Internet connection
u'http://www.google.com'
EmailField also access min_length and max_length parameters, for convenience. EmailField also access min_length and max_length parameters, for convenience.
>>> f = URLField(min_length=15, max_length=20) >>> f = URLField(min_length=15, max_length=20)
@ -1379,6 +1517,20 @@ Traceback (most recent call last):
... ...
ValidationError: [u'Select a valid choice. John is not one of the available choices.'] ValidationError: [u'Select a valid choice. John is not one of the available choices.']
# NullBooleanField ############################################################
>>> f = NullBooleanField()
>>> f.clean('')
>>> f.clean(True)
True
>>> f.clean(False)
False
>>> f.clean(None)
>>> f.clean('1')
>>> f.clean('2')
>>> f.clean('3')
>>> f.clean('hello')
# MultipleChoiceField ######################################################### # MultipleChoiceField #########################################################
>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')]) >>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')])
@ -1485,6 +1637,58 @@ u''
>>> f.clean(None) >>> f.clean(None)
u'' u''
# SplitDateTimeField ##########################################################
>>> f = SplitDateTimeField()
>>> f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)])
datetime.datetime(2006, 1, 10, 7, 30)
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean('hello')
Traceback (most recent call last):
...
ValidationError: [u'Enter a list of values.']
>>> f.clean(['hello', 'there'])
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid date.', u'Enter a valid time.']
>>> f.clean(['2006-01-10', 'there'])
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid time.']
>>> f.clean(['hello', '07:30'])
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid date.']
>>> f = SplitDateTimeField(required=False)
>>> f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)])
datetime.datetime(2006, 1, 10, 7, 30)
>>> f.clean(None)
>>> f.clean('')
>>> f.clean('hello')
Traceback (most recent call last):
...
ValidationError: [u'Enter a list of values.']
>>> f.clean(['hello', 'there'])
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid date.', u'Enter a valid time.']
>>> f.clean(['2006-01-10', 'there'])
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid time.']
>>> f.clean(['hello', '07:30'])
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid date.']
######### #########
# Forms # # Forms #
######### #########
@ -1502,6 +1706,8 @@ You can pass it data in __init__(), as a dictionary.
Pass a dictionary to a Form's __init__(). Pass a dictionary to a Form's __init__().
>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'}) >>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
>>> p.is_bound
True
>>> p.errors >>> p.errors
{} {}
>>> p.is_valid() >>> p.is_valid()
@ -1540,10 +1746,16 @@ Birthday 1940-10-9
Empty dictionaries are valid, too. Empty dictionaries are valid, too.
>>> p = Person({}) >>> p = Person({})
>>> p.is_bound
True
>>> p.errors >>> p.errors
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} {'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
>>> p.is_valid() >>> p.is_valid()
False False
>>> p.clean_data
Traceback (most recent call last):
...
AttributeError: 'Person' object has no attribute 'clean_data'
>>> print p >>> print p
<tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr> <tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr>
<tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr> <tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr>
@ -1565,13 +1777,19 @@ False
<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p> <p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p>
If you don't pass any values to the Form's __init__(), or if you pass None, If you don't pass any values to the Form's __init__(), or if you pass None,
the Form won't do any validation. Form.errors will be an empty dictionary *but* the Form will be considered unbound and won't do any validation. Form.errors
Form.is_valid() will return False. will be an empty dictionary *but* Form.is_valid() will return False.
>>> p = Person() >>> p = Person()
>>> p.is_bound
False
>>> p.errors >>> p.errors
{} {}
>>> p.is_valid() >>> p.is_valid()
False False
>>> p.clean_data
Traceback (most recent call last):
...
AttributeError: 'Person' object has no attribute 'clean_data'
>>> print p >>> print p
<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> <tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> <tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
@ -1611,8 +1829,9 @@ u'<ul class="errorlist"><li>first_name<ul class="errorlist"><li>This field is re
* birthday * birthday
* This field is required. * This field is required.
>>> p.clean_data >>> p.clean_data
>>> repr(p.clean_data) Traceback (most recent call last):
'None' ...
AttributeError: 'Person' object has no attribute 'clean_data'
>>> p['first_name'].errors >>> p['first_name'].errors
[u'This field is required.'] [u'This field is required.']
>>> p['first_name'].errors.as_ul() >>> p['first_name'].errors.as_ul()
@ -1628,6 +1847,17 @@ u'* This field is required.'
>>> print p['birthday'] >>> print p['birthday']
<input type="text" name="birthday" id="id_birthday" /> <input type="text" name="birthday" id="id_birthday" />
clean_data will always *only* contain a key for fields defined in the
Form, even if you pass extra data when you define the Form. In this
example, we pass a bunch of extra fields to the form constructor,
but clean_data contains only the form's fields.
>>> data = {'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9', 'extra1': 'hello', 'extra2': 'hello'}
>>> p = Person(data)
>>> p.is_valid()
True
>>> p.clean_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
"auto_id" tells the Form to add an "id" attribute to each form element. "auto_id" tells the Form to add an "id" attribute to each form element.
If it's a string that contains '%s', Django will use that as a format string If it's a string that contains '%s', Django will use that as a format string
into which the field's name will be inserted. It will also put a <label> around into which the field's name will be inserted. It will also put a <label> around
@ -1753,6 +1983,57 @@ For a form with a <select>, use ChoiceField:
<option value="J">Java</option> <option value="J">Java</option>
</select> </select>
You can specify widget attributes in the Widget constructor.
>>> class FrameworkForm(Form):
... name = CharField()
... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=Select(attrs={'class': 'foo'}))
>>> f = FrameworkForm(auto_id=False)
>>> print f['language']
<select class="foo" name="language">
<option value="P">Python</option>
<option value="J">Java</option>
</select>
>>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False)
>>> print f['language']
<select class="foo" name="language">
<option value="P" selected="selected">Python</option>
<option value="J">Java</option>
</select>
When passing a custom widget instance to ChoiceField, note that setting
'choices' on the widget is meaningless. The widget will use the choices
defined on the Field, not the ones defined on the Widget.
>>> class FrameworkForm(Form):
... name = CharField()
... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=Select(choices=[('R', 'Ruby'), ('P', 'Perl')], attrs={'class': 'foo'}))
>>> f = FrameworkForm(auto_id=False)
>>> print f['language']
<select class="foo" name="language">
<option value="P">Python</option>
<option value="J">Java</option>
</select>
>>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False)
>>> print f['language']
<select class="foo" name="language">
<option value="P" selected="selected">Python</option>
<option value="J">Java</option>
</select>
You can set a ChoiceField's choices after the fact.
>>> class FrameworkForm(Form):
... name = CharField()
... language = ChoiceField()
>>> f = FrameworkForm(auto_id=False)
>>> print f['language']
<select name="language">
</select>
>>> f.fields['language'].choices = [('P', 'Python'), ('J', 'Java')]
>>> print f['language']
<select name="language">
<option value="P">Python</option>
<option value="J">Java</option>
</select>
Add widget=RadioSelect to use that widget with a ChoiceField. Add widget=RadioSelect to use that widget with a ChoiceField.
>>> class FrameworkForm(Form): >>> class FrameworkForm(Form):
... name = CharField() ... name = CharField()
@ -1834,6 +2115,17 @@ MultipleChoiceField is a special case, as its data is required to be a list:
<option value="P" selected="selected">Paul McCartney</option> <option value="P" selected="selected">Paul McCartney</option>
</select> </select>
MultipleChoiceField rendered as_hidden() is a special case. Because it can
have multiple values, its as_hidden() renders multiple <input type="hidden">
tags.
>>> f = SongForm({'name': 'Yesterday', 'composers': ['P']}, auto_id=False)
>>> print f['composers'].as_hidden()
<input type="hidden" name="composers" value="P" />
>>> f = SongForm({'name': 'From Me To You', 'composers': ['P', 'J']}, auto_id=False)
>>> print f['composers'].as_hidden()
<input type="hidden" name="composers" value="P" />
<input type="hidden" name="composers" value="J" />
MultipleChoiceField can also be used with the CheckboxSelectMultiple widget. MultipleChoiceField can also be used with the CheckboxSelectMultiple widget.
>>> class SongForm(Form): >>> class SongForm(Form):
... name = CharField() ... name = CharField()
@ -1857,6 +2149,16 @@ MultipleChoiceField can also be used with the CheckboxSelectMultiple widget.
<li><label><input checked="checked" type="checkbox" name="composers" value="P" /> Paul McCartney</label></li> <li><label><input checked="checked" type="checkbox" name="composers" value="P" /> Paul McCartney</label></li>
</ul> </ul>
Regarding auto_id, CheckboxSelectMultiple is a special case. Each checkbox
gets a distinct ID, formed by appending an underscore plus the checkbox's
zero-based index.
>>> f = SongForm(auto_id='%s_id')
>>> print f['composers']
<ul>
<li><label><input type="checkbox" name="composers" value="J" id="composers_id_0" /> John Lennon</label></li>
<li><label><input type="checkbox" name="composers" value="P" id="composers_id_1" /> Paul McCartney</label></li>
</ul>
Data for a MultipleChoiceField should be a list. QueryDict and MultiValueDict Data for a MultipleChoiceField should be a list. QueryDict and MultiValueDict
conveniently work with this. conveniently work with this.
>>> data = {'name': 'Yesterday', 'composers': ['J', 'P']} >>> data = {'name': 'Yesterday', 'composers': ['J', 'P']}
@ -1869,11 +2171,20 @@ conveniently work with this.
>>> f.errors >>> f.errors
{} {}
>>> from django.utils.datastructures import MultiValueDict >>> from django.utils.datastructures import MultiValueDict
>>> data = MultiValueDict(dict(name='Yesterday', composers=['J', 'P'])) >>> data = MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P']))
>>> f = SongForm(data) >>> f = SongForm(data)
>>> f.errors >>> f.errors
{} {}
The MultipleHiddenInput widget renders multiple values as hidden fields.
>>> class SongFormHidden(Form):
... name = CharField()
... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=MultipleHiddenInput)
>>> f = SongFormHidden(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])), auto_id=False)
>>> print f.as_ul()
<li>Name: <input type="text" name="name" value="Yesterday" /><input type="hidden" name="composers" value="J" />
<input type="hidden" name="composers" value="P" /></li>
When using CheckboxSelectMultiple, the framework expects a list of input and When using CheckboxSelectMultiple, the framework expects a list of input and
returns a list of input. returns a list of input.
>>> f = SongForm({'name': 'Yesterday'}, auto_id=False) >>> f = SongForm({'name': 'Yesterday'}, auto_id=False)
@ -1890,6 +2201,8 @@ returns a list of input.
>>> f.clean_data >>> f.clean_data
{'composers': [u'J', u'P'], 'name': u'Yesterday'} {'composers': [u'J', u'P'], 'name': u'Yesterday'}
# Validating multiple fields in relation to another ###########################
There are a couple of ways to do multiple-field validation. If you want the There are a couple of ways to do multiple-field validation. If you want the
validation message to be associated with a particular field, implement the validation message to be associated with a particular field, implement the
clean_XXX() method on the Form, where XXX is the field name. As in clean_XXX() method on the Form, where XXX is the field name. As in
@ -1964,6 +2277,8 @@ Form.clean() is required to return a dictionary of all clean data.
>>> f.clean_data >>> f.clean_data
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} {'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
# Dynamic construction ########################################################
It's possible to construct a Form dynamically by adding to the self.fields It's possible to construct a Form dynamically by adding to the self.fields
dictionary in __init__(). Don't forget to call Form.__init__() within the dictionary in __init__(). Don't forget to call Form.__init__() within the
subclass' __init__(). subclass' __init__().
@ -1979,6 +2294,46 @@ subclass' __init__().
<tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr> <tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr>
<tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr> <tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr>
Instances of a dynamic Form do not persist fields from one Form instance to
the next.
>>> class MyForm(Form):
... def __init__(self, data=None, auto_id=False, field_list=[]):
... Form.__init__(self, data, auto_id)
... for field in field_list:
... self.fields[field[0]] = field[1]
>>> field_list = [('field1', CharField()), ('field2', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr>
<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr>
>>> field_list = [('field3', CharField()), ('field4', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr>
<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr>
>>> class MyForm(Form):
... default_field_1 = CharField()
... default_field_2 = CharField()
... def __init__(self, data=None, auto_id=False, field_list=[]):
... Form.__init__(self, data, auto_id)
... for field in field_list:
... self.fields[field[0]] = field[1]
>>> field_list = [('field1', CharField()), ('field2', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
<tr><th>Default field 1:</th><td><input type="text" name="default_field_1" /></td></tr>
<tr><th>Default field 2:</th><td><input type="text" name="default_field_2" /></td></tr>
<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr>
<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr>
>>> field_list = [('field3', CharField()), ('field4', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
<tr><th>Default field 1:</th><td><input type="text" name="default_field_1" /></td></tr>
<tr><th>Default field 2:</th><td><input type="text" name="default_field_2" /></td></tr>
<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr>
<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr>
HiddenInput widgets are displayed differently in the as_table(), as_ul() HiddenInput widgets are displayed differently in the as_table(), as_ul()
and as_p() output of a Form -- their verbose names are not displayed, and a and as_p() output of a Form -- their verbose names are not displayed, and a
separate row is not displayed. They're displayed in the last row of the separate row is not displayed. They're displayed in the last row of the
@ -2200,6 +2555,96 @@ validation error rather than using the initial value for 'username'.
>>> p.is_valid() >>> p.is_valid()
False False
# Dynamic initial data ########################################################
The previous technique dealt with "hard-coded" initial data, but it's also
possible to specify initial data after you've already created the Form class
(i.e., at runtime). Use the 'initial' parameter to the Form constructor. This
should be a dictionary containing initial values for one or more fields in the
form, keyed by field name.
>>> class UserRegistration(Form):
... username = CharField(max_length=10)
... password = CharField(widget=PasswordInput)
Here, we're not submitting any data, so the initial value will be displayed.
>>> p = UserRegistration(initial={'username': 'django'}, auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li>
<li>Password: <input type="password" name="password" /></li>
>>> p = UserRegistration(initial={'username': 'stephane'}, auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li>
<li>Password: <input type="password" name="password" /></li>
The 'initial' parameter is meaningless if you pass data.
>>> p = UserRegistration({}, initial={'username': 'django'}, auto_id=False)
>>> print p.as_ul()
<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li>
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
>>> p = UserRegistration({'username': u''}, initial={'username': 'django'}, auto_id=False)
>>> print p.as_ul()
<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li>
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
>>> p = UserRegistration({'username': u'foo'}, initial={'username': 'django'}, auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li>
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
A dynamic 'initial' value is *not* used as a fallback if data is not provided.
In this example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'})
>>> p.errors
{'username': [u'This field is required.']}
>>> p.is_valid()
False
If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(),
then the latter will get precedence.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, initial='django')
... password = CharField(widget=PasswordInput)
>>> p = UserRegistration(initial={'username': 'babik'}, auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li>
<li>Password: <input type="password" name="password" /></li>
# Help text ###################################################################
You can specify descriptive text for a field by using the 'help_text' argument
to a Field class. This help text is displayed when a Form is rendered.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, help_text='e.g., user@example.com')
... password = CharField(widget=PasswordInput, help_text='Choose wisely.')
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li>
<li>Password: <input type="password" name="password" /> Choose wisely.</li>
>>> print p.as_p()
<p>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</p>
<p>Password: <input type="password" name="password" /> Choose wisely.</p>
>>> print p.as_table()
<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /><br />e.g., user@example.com</td></tr>
<tr><th>Password:</th><td><input type="password" name="password" /><br />Choose wisely.</td></tr>
The help text is displayed whether or not data is provided for the form.
>>> p = UserRegistration({'username': u'foo'}, auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" value="foo" maxlength="10" /> e.g., user@example.com</li>
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /> Choose wisely.</li>
help_text is not displayed for hidden fields. It can be used for documentation
purposes, though.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, help_text='e.g., user@example.com')
... password = CharField(widget=PasswordInput)
... next = CharField(widget=HiddenInput, initial='/', help_text='Redirect destination')
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li>
<li>Password: <input type="password" name="password" /><input type="hidden" name="next" value="/" /></li>
# Forms with prefixes ######################################################### # Forms with prefixes #########################################################
Sometimes it's necessary to have multiple forms display on the same HTML page, Sometimes it's necessary to have multiple forms display on the same HTML page,
@ -2311,6 +2756,57 @@ True
>>> p.clean_data >>> p.clean_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
# Forms with NullBooleanFields ################################################
NullBooleanField is a bit of a special case because its presentation (widget)
is different than its data. This is handled transparently, though.
>>> class Person(Form):
... name = CharField()
... is_cool = NullBooleanField()
>>> p = Person({'name': u'Joe'}, auto_id=False)
>>> print p['is_cool']
<select name="is_cool">
<option value="1" selected="selected">Unknown</option>
<option value="2">Yes</option>
<option value="3">No</option>
</select>
>>> p = Person({'name': u'Joe', 'is_cool': u'1'}, auto_id=False)
>>> print p['is_cool']
<select name="is_cool">
<option value="1" selected="selected">Unknown</option>
<option value="2">Yes</option>
<option value="3">No</option>
</select>
>>> p = Person({'name': u'Joe', 'is_cool': u'2'}, auto_id=False)
>>> print p['is_cool']
<select name="is_cool">
<option value="1">Unknown</option>
<option value="2" selected="selected">Yes</option>
<option value="3">No</option>
</select>
>>> p = Person({'name': u'Joe', 'is_cool': u'3'}, auto_id=False)
>>> print p['is_cool']
<select name="is_cool">
<option value="1">Unknown</option>
<option value="2">Yes</option>
<option value="3" selected="selected">No</option>
</select>
>>> p = Person({'name': u'Joe', 'is_cool': True}, auto_id=False)
>>> print p['is_cool']
<select name="is_cool">
<option value="1">Unknown</option>
<option value="2" selected="selected">Yes</option>
<option value="3">No</option>
</select>
>>> p = Person({'name': u'Joe', 'is_cool': False}, auto_id=False)
>>> print p['is_cool']
<select name="is_cool">
<option value="1">Unknown</option>
<option value="2">Yes</option>
<option value="3" selected="selected">No</option>
</select>
# Basic form processing in a view ############################################# # Basic form processing in a view #############################################
>>> from django.template import Template, Context >>> from django.template import Template, Context
@ -2439,6 +2935,15 @@ field an "id" attribute.
<input type="submit" /> <input type="submit" />
</form> </form>
The label_tag() method takes an optional attrs argument: a dictionary of HTML
attributes to add to the <label> tag.
>>> f = UserRegistration(auto_id='id_%s')
>>> for bf in f:
... print bf.label_tag(attrs={'class': 'pretty'})
<label for="id_username" class="pretty">Username</label>
<label for="id_password1" class="pretty">Password1</label>
<label for="id_password2" class="pretty">Password2</label>
To display the errors that aren't associated with a particular field -- e.g., To display the errors that aren't associated with a particular field -- e.g.,
the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the
template. If used on its own, it is displayed as a <ul> (or an empty string, if template. If used on its own, it is displayed as a <ul> (or an empty string, if