mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +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:
parent
92b7851424
commit
e17f755514
@ -25,7 +25,7 @@
|
||||
</p>
|
||||
{% 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">
|
||||
|
||||
|
@ -101,6 +101,10 @@ def result_headers(cl):
|
||||
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
||||
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
|
||||
|
||||
def _boolean_icon(field_val):
|
||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||
return '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
||||
|
||||
def items_for_result(cl, result):
|
||||
first = True
|
||||
pk = cl.lookup_opts.pk.attname
|
||||
@ -114,9 +118,14 @@ def items_for_result(cl, result):
|
||||
try:
|
||||
attr = getattr(result, field_name)
|
||||
allow_tags = getattr(attr, 'allow_tags', False)
|
||||
boolean = getattr(attr, 'boolean', False)
|
||||
if callable(attr):
|
||||
attr = attr()
|
||||
result_repr = str(attr)
|
||||
if boolean:
|
||||
allow_tags = True
|
||||
result_repr = _boolean_icon(attr)
|
||||
else:
|
||||
result_repr = str(attr)
|
||||
except (AttributeError, ObjectDoesNotExist):
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
else:
|
||||
@ -147,8 +156,7 @@ def items_for_result(cl, result):
|
||||
row_class = ' class="nowrap"'
|
||||
# Booleans are special: We use images.
|
||||
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
|
||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
||||
result_repr = _boolean_icon(field_val)
|
||||
# FloatFields are special: Zero-pad the decimals.
|
||||
elif isinstance(f, models.FloatField):
|
||||
if field_val is not None:
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
CONTENT_TYPE_CACHE = {}
|
||||
class ContentTypeManager(models.Manager):
|
||||
def get_for_model(self, model):
|
||||
"""
|
||||
@ -8,10 +9,15 @@ class ContentTypeManager(models.Manager):
|
||||
ContentType if necessary.
|
||||
"""
|
||||
opts = model._meta
|
||||
# The str() is needed around opts.verbose_name because it's a
|
||||
# django.utils.functional.__proxy__ object.
|
||||
ct, created = self.model._default_manager.get_or_create(app_label=opts.app_label,
|
||||
model=opts.object_name.lower(), defaults={'name': str(opts.verbose_name)})
|
||||
key = (opts.app_label, opts.object_name.lower())
|
||||
try:
|
||||
ct = CONTENT_TYPE_CACHE[key]
|
||||
except KeyError:
|
||||
# The str() is needed around opts.verbose_name because it's a
|
||||
# django.utils.functional.__proxy__ object.
|
||||
ct, created = self.model._default_manager.get_or_create(app_label=key[0],
|
||||
model=key[1], defaults={'name': str(opts.verbose_name)})
|
||||
CONTENT_TYPE_CACHE[key] = ct
|
||||
return ct
|
||||
|
||||
class ContentType(models.Model):
|
||||
|
@ -1,5 +1,6 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.models import Session
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.utils.cache import patch_vary_headers
|
||||
import datetime
|
||||
|
||||
@ -55,7 +56,7 @@ class SessionWrapper(object):
|
||||
s = Session.objects.get(session_key=self.session_key,
|
||||
expire_date__gt=datetime.datetime.now())
|
||||
self._session_cache = s.get_decoded()
|
||||
except Session.DoesNotExist:
|
||||
except (Session.DoesNotExist, SuspiciousOperation):
|
||||
self._session_cache = {}
|
||||
# Set the session_key to None to force creation of a new
|
||||
# key, for extra security.
|
||||
|
6
django/core/cache/backends/dummy.py
vendored
6
django/core/cache/backends/dummy.py
vendored
@ -6,8 +6,8 @@ class CacheClass(BaseCache):
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
pass
|
||||
def get(self, key, default=None):
|
||||
return default
|
||||
|
||||
def set(self, *args, **kwargs):
|
||||
pass
|
||||
@ -16,7 +16,7 @@ class CacheClass(BaseCache):
|
||||
pass
|
||||
|
||||
def get_many(self, *args, **kwargs):
|
||||
pass
|
||||
return {}
|
||||
|
||||
def has_key(self, *args, **kwargs):
|
||||
return False
|
||||
|
@ -25,7 +25,7 @@ APP_ARGS = '[appname ...]'
|
||||
# which has been installed.
|
||||
PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
|
||||
|
||||
INVALID_PROJECT_NAMES = ('django', 'test')
|
||||
INVALID_PROJECT_NAMES = ('django', 'site', 'test')
|
||||
|
||||
# Set up the terminal color scheme.
|
||||
class dummy: pass
|
||||
@ -735,7 +735,7 @@ def startproject(project_name, directory):
|
||||
"Creates a Django project for the given project_name in the given directory."
|
||||
from random import choice
|
||||
if project_name in INVALID_PROJECT_NAMES:
|
||||
sys.stderr.write(style.ERROR("Error: %r isn't a valid project name. Please try another.\n" % project_name))
|
||||
sys.stderr.write(style.ERROR("Error: '%r' conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name.\n" % project_name))
|
||||
sys.exit(1)
|
||||
_start_helper('project', project_name, directory)
|
||||
# Create a random SECRET_KEY hash, and put it in the main settings.
|
||||
|
@ -57,7 +57,7 @@ def Deserializer(object_list, **options):
|
||||
for d in object_list:
|
||||
# Look up the model and starting build a dict of data for it.
|
||||
Model = _get_model(d["model"])
|
||||
data = {Model._meta.pk.name : d["pk"]}
|
||||
data = {Model._meta.pk.attname : d["pk"]}
|
||||
m2m_data = {}
|
||||
|
||||
# Handle each field
|
||||
|
@ -21,6 +21,5 @@ DATA_TYPES = {
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'text',
|
||||
'TimeField': 'time',
|
||||
'URLField': 'varchar(200)',
|
||||
'USStateField': 'varchar(2)',
|
||||
}
|
||||
|
@ -25,6 +25,5 @@ DATA_TYPES = {
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'longtext',
|
||||
'TimeField': 'time',
|
||||
'URLField': 'varchar(200)',
|
||||
'USStateField': 'varchar(2)',
|
||||
}
|
||||
|
@ -25,6 +25,5 @@ DATA_TYPES = {
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'text',
|
||||
'TimeField': 'time',
|
||||
'URLField': 'varchar(200)',
|
||||
'USStateField': 'varchar(2)',
|
||||
}
|
||||
|
@ -24,6 +24,5 @@ DATA_TYPES = {
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'text',
|
||||
'TimeField': 'time',
|
||||
'URLField': 'varchar(200)',
|
||||
'USStateField': 'varchar(2)',
|
||||
}
|
||||
|
@ -337,11 +337,11 @@ class Field(object):
|
||||
return self._choices
|
||||
choices = property(_get_choices)
|
||||
|
||||
def formfield(self, initial=None):
|
||||
def formfield(self, **kwargs):
|
||||
"Returns a django.newforms.Field instance for this database Field."
|
||||
from django.newforms import CharField
|
||||
# TODO: This is just a temporary default during development.
|
||||
return forms.CharField(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.CharField(**defaults)
|
||||
|
||||
def value_from_object(self, obj):
|
||||
"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)
|
||||
cls._meta.has_auto_field = True
|
||||
|
||||
def formfield(self, initial=None):
|
||||
def formfield(self, **kwargs):
|
||||
return None
|
||||
|
||||
class BooleanField(Field):
|
||||
@ -400,8 +400,10 @@ class BooleanField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [oldforms.CheckboxField]
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.BooleanField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.BooleanField(**defaults)
|
||||
|
||||
class CharField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
@ -417,8 +419,10 @@ class CharField(Field):
|
||||
raise validators.ValidationError, gettext_lazy("This field cannot be null.")
|
||||
return str(value)
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.CharField(max_length=self.maxlength, required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'max_length': self.maxlength, 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.CharField(**defaults)
|
||||
|
||||
# TODO: Maybe move this into contrib, because it's specialized.
|
||||
class CommaSeparatedIntegerField(CharField):
|
||||
@ -497,8 +501,10 @@ class DateField(Field):
|
||||
val = self._get_val_from_obj(obj)
|
||||
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.DateField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.DateField(**defaults)
|
||||
|
||||
class DateTimeField(DateField):
|
||||
def to_python(self, value):
|
||||
@ -569,8 +575,10 @@ class DateTimeField(DateField):
|
||||
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
|
||||
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.DateTimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.DateTimeField(**defaults)
|
||||
|
||||
class EmailField(CharField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -586,8 +594,10 @@ class EmailField(CharField):
|
||||
def validate(self, field_data, all_data):
|
||||
validators.isValidEmail(field_data, all_data)
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.EmailField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.EmailField(**defaults)
|
||||
|
||||
class FileField(Field):
|
||||
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
|
||||
@ -721,8 +731,10 @@ class IntegerField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [oldforms.IntegerField]
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.IntegerField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.IntegerField(**defaults)
|
||||
|
||||
class IPAddressField(Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -778,6 +790,11 @@ class TextField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [oldforms.LargeTextField]
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'widget': forms.Textarea, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.CharField(**defaults)
|
||||
|
||||
class TimeField(Field):
|
||||
empty_strings_allowed = False
|
||||
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)
|
||||
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.TimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.TimeField(**defaults)
|
||||
|
||||
class URLField(Field):
|
||||
class URLField(CharField):
|
||||
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
|
||||
kwargs['maxlength'] = kwargs.get('maxlength', 200)
|
||||
if verify_exists:
|
||||
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
|
||||
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):
|
||||
return [oldforms.URLField]
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.URLField(required=not self.blank, verify_exists=self.verify_exists, label=capfirst(self.verbose_name), initial=initial)
|
||||
def get_internal_type(self):
|
||||
return "CharField"
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'required': not self.blank, 'verify_exists': self.verify_exists, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.URLField(**defaults)
|
||||
|
||||
class USStateField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
|
@ -316,18 +316,20 @@ def create_many_related_manager(superclass):
|
||||
# join_table: name of the m2m link table
|
||||
# source_col_name: the PK colname in join_table for the source object
|
||||
# target_col_name: the PK colname in join_table for the target object
|
||||
# *objs - objects to add
|
||||
# *objs - objects to add. Either object instances, or primary keys of object instances.
|
||||
from django.db import connection
|
||||
|
||||
# If there aren't any objects, there is nothing to do.
|
||||
if objs:
|
||||
# Check that all the objects are of the right type
|
||||
new_ids = set()
|
||||
for obj in objs:
|
||||
if not isinstance(obj, self.model):
|
||||
raise ValueError, "objects to add() must be %s instances" % self.model._meta.object_name
|
||||
if isinstance(obj, self.model):
|
||||
new_ids.add(obj._get_pk_val())
|
||||
else:
|
||||
new_ids.add(obj)
|
||||
# Add the newly created or already existing objects to the join table.
|
||||
# First find out which items are already added, to avoid adding them twice
|
||||
new_ids = set([obj._get_pk_val() for obj in objs])
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||
(target_col_name, self.join_table, source_col_name,
|
||||
@ -354,11 +356,13 @@ def create_many_related_manager(superclass):
|
||||
# If there aren't any objects, there is nothing to do.
|
||||
if objs:
|
||||
# Check that all the objects are of the right type
|
||||
old_ids = set()
|
||||
for obj in objs:
|
||||
if not isinstance(obj, self.model):
|
||||
raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name
|
||||
if isinstance(obj, self.model):
|
||||
old_ids.add(obj._get_pk_val())
|
||||
else:
|
||||
old_ids.add(obj)
|
||||
# Remove the specified objects from the join table
|
||||
old_ids = set([obj._get_pk_val() for obj in objs])
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||
(self.join_table, source_col_name,
|
||||
@ -548,8 +552,10 @@ class ForeignKey(RelatedField, Field):
|
||||
def contribute_to_related_class(self, cls, related):
|
||||
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.ChoiceField(**defaults)
|
||||
|
||||
class OneToOneField(RelatedField, IntegerField):
|
||||
def __init__(self, to, to_field=None, **kwargs):
|
||||
@ -612,8 +618,10 @@ class OneToOneField(RelatedField, IntegerField):
|
||||
if not cls._meta.one_to_one_field:
|
||||
cls._meta.one_to_one_field = self
|
||||
|
||||
def formfield(self, initial=None):
|
||||
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.ChoiceField(**kwargs)
|
||||
|
||||
class ManyToManyField(RelatedField, Field):
|
||||
def __init__(self, to, **kwargs):
|
||||
@ -625,6 +633,7 @@ class ManyToManyField(RelatedField, Field):
|
||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||
raw_id_admin=kwargs.pop('raw_id_admin', False),
|
||||
symmetrical=kwargs.pop('symmetrical', True))
|
||||
self.db_table = kwargs.pop('db_table', None)
|
||||
if kwargs["rel"].raw_id_admin:
|
||||
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
|
||||
Field.__init__(self, **kwargs)
|
||||
@ -647,10 +656,10 @@ class ManyToManyField(RelatedField, Field):
|
||||
|
||||
def _get_m2m_db_table(self, opts):
|
||||
"Function that can be curried to provide the m2m table name for this relation"
|
||||
from django.db import backend
|
||||
from django.db.backends.util import truncate_name
|
||||
name = '%s_%s' % (opts.db_table, self.name)
|
||||
return truncate_name(name, backend.get_max_name_length())
|
||||
if self.db_table:
|
||||
return self.db_table
|
||||
else:
|
||||
return '%s_%s' % (opts.db_table, self.name)
|
||||
|
||||
def _get_m2m_column_name(self, related):
|
||||
"Function that can be curried to provide the source column name for the m2m table"
|
||||
@ -728,12 +737,14 @@ class ManyToManyField(RelatedField, Field):
|
||||
"Returns the value of this field in the given model instance."
|
||||
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
|
||||
# MultipleChoiceField takes a list of IDs.
|
||||
if initial is not None:
|
||||
initial = [i._get_pk_val() for i in initial]
|
||||
return forms.MultipleChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
||||
if kwargs.get('initial') is not None:
|
||||
kwargs['initial'] = [i._get_pk_val() for i in kwargs['initial']]
|
||||
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||
defaults.update(kwargs)
|
||||
return forms.MultipleChoiceField(**defaults)
|
||||
|
||||
class ManyToOneRel(object):
|
||||
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.db.models.query import QuerySet
|
||||
from django.db.models.query import QuerySet, EmptyQuerySet
|
||||
from django.dispatch import dispatcher
|
||||
from django.db.models import signals
|
||||
from django.db.models.fields import FieldDoesNotExist
|
||||
@ -41,12 +41,18 @@ class Manager(object):
|
||||
#######################
|
||||
# PROXIES TO QUERYSET #
|
||||
#######################
|
||||
|
||||
def get_empty_query_set(self):
|
||||
return EmptyQuerySet(self.model)
|
||||
|
||||
def get_query_set(self):
|
||||
"""Returns a new QuerySet object. Subclasses can override this method
|
||||
to easily customise the behaviour of the Manager.
|
||||
"""
|
||||
return QuerySet(self.model)
|
||||
|
||||
def none(self):
|
||||
return self.get_empty_query_set()
|
||||
|
||||
def all(self):
|
||||
return self.get_query_set()
|
||||
|
@ -1,5 +1,6 @@
|
||||
from django.db import backend, connection, get_query_module, transaction
|
||||
from django.db.models.fields import DateField, FieldDoesNotExist
|
||||
from django.db.models.fields.generic import GenericRelation
|
||||
from django.db.models import signals
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils.datastructures import SortedDict
|
||||
@ -25,6 +26,9 @@ QUERY_TERMS = (
|
||||
# Larger values are slightly faster at the expense of more storage space.
|
||||
GET_ITERATOR_CHUNK_SIZE = 100
|
||||
|
||||
class EmptyResultSet(Exception):
|
||||
pass
|
||||
|
||||
####################
|
||||
# HELPER FUNCTIONS #
|
||||
####################
|
||||
@ -169,7 +173,11 @@ class _QuerySet(object):
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
select, sql, params, full_query = self._get_sql_clause()
|
||||
try:
|
||||
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)
|
||||
|
||||
fill_cache = self._select_related
|
||||
@ -194,7 +202,12 @@ class _QuerySet(object):
|
||||
counter._offset = None
|
||||
counter._limit = None
|
||||
counter._select_related = False
|
||||
select, sql, params, full_query = counter._get_sql_clause()
|
||||
|
||||
try:
|
||||
select, sql, params, full_query = counter._get_sql_clause()
|
||||
except EmptyResultSet:
|
||||
return 0
|
||||
|
||||
cursor = connection.cursor()
|
||||
if self._distinct:
|
||||
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
|
||||
@ -532,7 +545,12 @@ class ValuesQuerySet(QuerySet):
|
||||
field_names = [f.attname for f in self.model._meta.fields]
|
||||
|
||||
cursor = connection.cursor()
|
||||
select, sql, params, full_query = self._get_sql_clause()
|
||||
|
||||
try:
|
||||
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]
|
||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||
while 1:
|
||||
@ -554,19 +572,25 @@ class DateQuerySet(QuerySet):
|
||||
if self._field.null:
|
||||
self._where.append('%s.%s IS NOT NULL' % \
|
||||
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
|
||||
select, sql, params, full_query = self._get_sql_clause()
|
||||
try:
|
||||
select, sql, params, full_query = self._get_sql_clause()
|
||||
except EmptyResultSet:
|
||||
raise StopIteration
|
||||
|
||||
table_name = backend.quote_name(self.model._meta.db_table)
|
||||
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:
|
||||
group_by = '1'
|
||||
else:
|
||||
group_by = date_trunc_sql
|
||||
fmt = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s'
|
||||
stmt = fmt % (date_trunc_sql, sql, group_by, self._order)
|
||||
group_by = backend.get_date_trunc_sql(self._kind,
|
||||
'%s.%s' % (table_name, field_name))
|
||||
|
||||
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.execute(stmt, params)
|
||||
cursor.execute(sql, params)
|
||||
if backend.needs_datetime_string_cast:
|
||||
return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
|
||||
else:
|
||||
@ -579,6 +603,25 @@ class DateQuerySet(QuerySet):
|
||||
c._order = self._order
|
||||
return c
|
||||
|
||||
class EmptyQuerySet(QuerySet):
|
||||
def __init__(self, model=None):
|
||||
super(EmptyQuerySet, self).__init__(model)
|
||||
self._result_cache = []
|
||||
|
||||
def iterator(self):
|
||||
raise StopIteration
|
||||
|
||||
def count(self):
|
||||
return 0
|
||||
|
||||
def delete(self):
|
||||
pass
|
||||
|
||||
def _clone(self, klass=None, **kwargs):
|
||||
c = super(EmptyQuerySet, self)._clone(klass, **kwargs)
|
||||
c._result_cache = []
|
||||
return c
|
||||
|
||||
class QOperator(object):
|
||||
"Base class for QAnd and QOr"
|
||||
def __init__(self, *args):
|
||||
@ -587,10 +630,14 @@ class QOperator(object):
|
||||
def get_sql(self, opts):
|
||||
joins, where, params = SortedDict(), [], []
|
||||
for val in self.args:
|
||||
joins2, where2, params2 = val.get_sql(opts)
|
||||
joins.update(joins2)
|
||||
where.extend(where2)
|
||||
params.extend(params2)
|
||||
try:
|
||||
joins2, where2, params2 = val.get_sql(opts)
|
||||
joins.update(joins2)
|
||||
where.extend(where2)
|
||||
params.extend(params2)
|
||||
except EmptyResultSet:
|
||||
if not isinstance(self, QOr):
|
||||
raise EmptyResultSet
|
||||
if where:
|
||||
return joins, ['(%s)' % self.operator.join(where)], params
|
||||
return joins, [], params
|
||||
@ -644,8 +691,11 @@ class QNot(Q):
|
||||
self.q = q
|
||||
|
||||
def get_sql(self, opts):
|
||||
joins, where, params = self.q.get_sql(opts)
|
||||
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
||||
try:
|
||||
joins, where, params = self.q.get_sql(opts)
|
||||
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
||||
except EmptyResultSet:
|
||||
return SortedDict(), [], []
|
||||
return joins, where2, params
|
||||
|
||||
def get_where_clause(lookup_type, table_prefix, field_name, value):
|
||||
@ -662,7 +712,11 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
|
||||
except KeyError:
|
||||
pass
|
||||
if lookup_type == 'in':
|
||||
return '%s%s IN (%s)' % (table_prefix, field_name, ','.join(['%s' for v in value]))
|
||||
in_string = ','.join(['%s' for id in value])
|
||||
if in_string:
|
||||
return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
|
||||
else:
|
||||
raise EmptyResultSet
|
||||
elif lookup_type == 'range':
|
||||
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
|
||||
elif lookup_type in ('year', 'month', 'day'):
|
||||
@ -948,18 +1002,26 @@ def delete_objects(seen_objs):
|
||||
|
||||
pk_list = [pk for pk,instance in seen_objs[cls]]
|
||||
for related in cls._meta.get_all_related_many_to_many_objects():
|
||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||
(qn(related.field.m2m_db_table()),
|
||||
qn(related.field.m2m_reverse_name()),
|
||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
||||
if not isinstance(related.field, GenericRelation):
|
||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||
(qn(related.field.m2m_db_table()),
|
||||
qn(related.field.m2m_reverse_name()),
|
||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
||||
for f in cls._meta.many_to_many:
|
||||
if isinstance(f, GenericRelation):
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
|
||||
args_extra = [ContentType.objects.get_for_model(cls).id]
|
||||
else:
|
||||
query_extra = ''
|
||||
args_extra = []
|
||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||
cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
|
||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra,
|
||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
|
||||
for field in cls._meta.fields:
|
||||
if field.rel and field.null and field.rel.to in seen_objs:
|
||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||
|
@ -3,8 +3,8 @@ Field classes
|
||||
"""
|
||||
|
||||
from django.utils.translation import gettext
|
||||
from util import ValidationError, smart_unicode
|
||||
from widgets import TextInput, PasswordInput, CheckboxInput, Select, SelectMultiple
|
||||
from util import ErrorList, ValidationError, smart_unicode
|
||||
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
|
||||
import datetime
|
||||
import re
|
||||
import time
|
||||
@ -15,8 +15,9 @@ __all__ = (
|
||||
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
|
||||
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
|
||||
'RegexField', 'EmailField', 'URLField', 'BooleanField',
|
||||
'ChoiceField', 'MultipleChoiceField',
|
||||
'ComboField',
|
||||
'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
|
||||
'ComboField', 'MultiValueField',
|
||||
'SplitDateTimeField',
|
||||
)
|
||||
|
||||
# These values, if given to to_python(), will trigger the self.required check.
|
||||
@ -29,11 +30,12 @@ except NameError:
|
||||
|
||||
class Field(object):
|
||||
widget = TextInput # Default widget to use when rendering this type of Field.
|
||||
hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
|
||||
|
||||
# Tracks each time a Field instance is created. Used to retain order.
|
||||
creation_counter = 0
|
||||
|
||||
def __init__(self, required=True, widget=None, 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.
|
||||
# True by default.
|
||||
# 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.
|
||||
# initial -- A value to use in this Field's initial display. This value is
|
||||
# *not* used as a fallback if data isn't given.
|
||||
# help_text -- An optional string to use as "help text" for this Field.
|
||||
if label is not None:
|
||||
label = smart_unicode(label)
|
||||
self.required, self.label, self.initial = required, label, initial
|
||||
self.help_text = help_text
|
||||
widget = widget or self.widget
|
||||
if isinstance(widget, type):
|
||||
widget = widget()
|
||||
@ -83,17 +87,15 @@ class Field(object):
|
||||
return {}
|
||||
|
||||
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
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
super(CharField, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
"Validates max_length and min_length. Returns a Unicode object."
|
||||
Field.clean(self, value)
|
||||
super(CharField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
value = u''
|
||||
if not self.required:
|
||||
return value
|
||||
return u''
|
||||
value = smart_unicode(value)
|
||||
if self.max_length is not None and len(value) > self.max_length:
|
||||
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||
@ -106,18 +108,18 @@ class CharField(Field):
|
||||
return {'maxlength': str(self.max_length)}
|
||||
|
||||
class IntegerField(Field):
|
||||
def __init__(self, max_value=None, min_value=None, required=True, widget=None, label=None, initial=None):
|
||||
def __init__(self, max_value=None, min_value=None, *args, **kwargs):
|
||||
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):
|
||||
"""
|
||||
Validates that int() can be called on the input. Returns the result
|
||||
of int().
|
||||
of int(). Returns None for empty values.
|
||||
"""
|
||||
super(IntegerField, self).clean(value)
|
||||
if not self.required and value in EMPTY_VALUES:
|
||||
return u''
|
||||
if value in EMPTY_VALUES:
|
||||
return None
|
||||
try:
|
||||
value = int(value)
|
||||
except (ValueError, TypeError):
|
||||
@ -137,8 +139,8 @@ DEFAULT_DATE_INPUT_FORMATS = (
|
||||
)
|
||||
|
||||
class DateField(Field):
|
||||
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
def __init__(self, input_formats=None, *args, **kwargs):
|
||||
super(DateField, self).__init__(*args, **kwargs)
|
||||
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
||||
|
||||
def clean(self, value):
|
||||
@ -146,7 +148,7 @@ class DateField(Field):
|
||||
Validates that the input can be converted to a date. Returns a Python
|
||||
datetime.date object.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
super(DateField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return None
|
||||
if isinstance(value, datetime.datetime):
|
||||
@ -166,8 +168,8 @@ DEFAULT_TIME_INPUT_FORMATS = (
|
||||
)
|
||||
|
||||
class TimeField(Field):
|
||||
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
def __init__(self, input_formats=None, *args, **kwargs):
|
||||
super(TimeField, self).__init__(*args, **kwargs)
|
||||
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
|
||||
|
||||
def clean(self, value):
|
||||
@ -175,7 +177,7 @@ class TimeField(Field):
|
||||
Validates that the input can be converted to a time. Returns a Python
|
||||
datetime.time object.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
super(TimeField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return None
|
||||
if isinstance(value, datetime.time):
|
||||
@ -200,8 +202,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
|
||||
)
|
||||
|
||||
class DateTimeField(Field):
|
||||
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
def __init__(self, input_formats=None, *args, **kwargs):
|
||||
super(DateTimeField, self).__init__(*args, **kwargs)
|
||||
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
||||
|
||||
def clean(self, value):
|
||||
@ -209,7 +211,7 @@ class DateTimeField(Field):
|
||||
Validates that the input can be converted to a datetime. Returns a
|
||||
Python datetime.datetime object.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
super(DateTimeField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return None
|
||||
if isinstance(value, datetime.datetime):
|
||||
@ -224,14 +226,13 @@ class DateTimeField(Field):
|
||||
raise ValidationError(gettext(u'Enter a valid date/time.'))
|
||||
|
||||
class RegexField(Field):
|
||||
def __init__(self, regex, max_length=None, min_length=None, error_message=None,
|
||||
required=True, widget=None, label=None, initial=None):
|
||||
def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
|
||||
"""
|
||||
regex can be either a string or a compiled regular expression object.
|
||||
error_message is an optional error message to use, if
|
||||
'Enter a valid value' is too generic for you.
|
||||
"""
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
super(RegexField, self).__init__(*args, **kwargs)
|
||||
if isinstance(regex, basestring):
|
||||
regex = re.compile(regex)
|
||||
self.regex = regex
|
||||
@ -243,10 +244,11 @@ class RegexField(Field):
|
||||
Validates that the input matches the regular expression. Returns a
|
||||
Unicode object.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
if value in EMPTY_VALUES: value = u''
|
||||
super(RegexField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
value = u''
|
||||
value = smart_unicode(value)
|
||||
if not self.required and value == u'':
|
||||
if value == u'':
|
||||
return value
|
||||
if self.max_length is not None and len(value) > self.max_length:
|
||||
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||
@ -262,8 +264,9 @@ email_re = re.compile(
|
||||
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
|
||||
|
||||
class EmailField(RegexField):
|
||||
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None, initial=None):
|
||||
RegexField.__init__(self, email_re, max_length, min_length, gettext(u'Enter a valid e-mail address.'), required, widget, label, initial)
|
||||
def __init__(self, max_length=None, min_length=None, *args, **kwargs):
|
||||
RegexField.__init__(self, email_re, max_length, min_length,
|
||||
gettext(u'Enter a valid e-mail address.'), *args, **kwargs)
|
||||
|
||||
url_re = re.compile(
|
||||
r'^https?://' # http:// or https://
|
||||
@ -279,14 +282,16 @@ except ImportError:
|
||||
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
||||
|
||||
class URLField(RegexField):
|
||||
def __init__(self, max_length=None, min_length=None, required=True, verify_exists=False, widget=None, label=None,
|
||||
initial=None, validator_user_agent=URL_VALIDATOR_USER_AGENT):
|
||||
RegexField.__init__(self, url_re, max_length, min_length, gettext(u'Enter a valid URL.'), required, widget, label, initial)
|
||||
def __init__(self, max_length=None, min_length=None, verify_exists=False,
|
||||
validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
|
||||
super(URLField, self).__init__(url_re, max_length, min_length, gettext(u'Enter a valid URL.'), *args, **kwargs)
|
||||
self.verify_exists = verify_exists
|
||||
self.user_agent = validator_user_agent
|
||||
|
||||
def clean(self, value):
|
||||
value = RegexField.clean(self, value)
|
||||
value = super(URLField, self).clean(value)
|
||||
if value == u'':
|
||||
return value
|
||||
if self.verify_exists:
|
||||
import urllib2
|
||||
from django.conf import settings
|
||||
@ -311,24 +316,43 @@ class BooleanField(Field):
|
||||
|
||||
def clean(self, value):
|
||||
"Returns a Python boolean object."
|
||||
Field.clean(self, value)
|
||||
super(BooleanField, self).clean(value)
|
||||
return bool(value)
|
||||
|
||||
class NullBooleanField(BooleanField):
|
||||
"""
|
||||
A field whose valid values are None, True and False. Invalid values are
|
||||
cleaned to None.
|
||||
"""
|
||||
widget = NullBooleanSelect
|
||||
|
||||
def clean(self, value):
|
||||
return {True: True, False: False}.get(value, None)
|
||||
|
||||
class ChoiceField(Field):
|
||||
def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None):
|
||||
if isinstance(widget, type):
|
||||
widget = widget(choices=choices)
|
||||
Field.__init__(self, required, widget, label, initial)
|
||||
def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None, help_text=None):
|
||||
super(ChoiceField, self).__init__(required, widget, label, initial, help_text)
|
||||
self.choices = choices
|
||||
|
||||
def _get_choices(self):
|
||||
return self._choices
|
||||
|
||||
def _set_choices(self, value):
|
||||
# Setting choices also sets the choices on the widget.
|
||||
self._choices = value
|
||||
self.widget.choices = value
|
||||
|
||||
choices = property(_get_choices, _set_choices)
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Validates that the input is in self.choices.
|
||||
"""
|
||||
value = Field.clean(self, value)
|
||||
if value in EMPTY_VALUES: value = u''
|
||||
value = super(ChoiceField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
value = u''
|
||||
value = smart_unicode(value)
|
||||
if not self.required and value == u'':
|
||||
if value == u'':
|
||||
return value
|
||||
valid_values = set([str(k) for k, v in self.choices])
|
||||
if value not in valid_values:
|
||||
@ -336,8 +360,10 @@ class ChoiceField(Field):
|
||||
return value
|
||||
|
||||
class MultipleChoiceField(ChoiceField):
|
||||
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None):
|
||||
ChoiceField.__init__(self, choices, required, widget, label, initial)
|
||||
hidden_widget = MultipleHiddenInput
|
||||
|
||||
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None, help_text=None):
|
||||
super(MultipleChoiceField, self).__init__(choices, required, widget, label, initial, help_text)
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
@ -361,8 +387,11 @@ class MultipleChoiceField(ChoiceField):
|
||||
return new_value
|
||||
|
||||
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
|
||||
# required validation will be handled by ComboField, not by those
|
||||
# individual fields.
|
||||
@ -375,7 +404,88 @@ class ComboField(Field):
|
||||
Validates the given value against all of self.fields, which is a
|
||||
list of Field instances.
|
||||
"""
|
||||
Field.clean(self, value)
|
||||
super(ComboField, self).clean(value)
|
||||
for field in self.fields:
|
||||
value = field.clean(value)
|
||||
return value
|
||||
|
||||
class MultiValueField(Field):
|
||||
"""
|
||||
A Field that is composed of multiple Fields.
|
||||
|
||||
Its clean() method takes a "decompressed" list of values. Each value in
|
||||
this list is cleaned by the corresponding field -- the first value is
|
||||
cleaned by the first field, the second value is cleaned by the second
|
||||
field, etc. Once all fields are cleaned, the list of clean values is
|
||||
"compressed" into a single value.
|
||||
|
||||
Subclasses should implement compress(), which specifies how a list of
|
||||
valid values should be converted to a single value. Subclasses should not
|
||||
have to implement clean().
|
||||
|
||||
You'll probably want to use this with MultiWidget.
|
||||
"""
|
||||
def __init__(self, fields=(), *args, **kwargs):
|
||||
super(MultiValueField, self).__init__(*args, **kwargs)
|
||||
# Set 'required' to False on the individual fields, because the
|
||||
# required validation will be handled by MultiValueField, not by those
|
||||
# individual fields.
|
||||
for f in fields:
|
||||
f.required = False
|
||||
self.fields = fields
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Validates every value in the given list. A value is validated against
|
||||
the corresponding Field in self.fields.
|
||||
|
||||
For example, if this MultiValueField was instantiated with
|
||||
fields=(DateField(), TimeField()), clean() would call
|
||||
DateField.clean(value[0]) and TimeField.clean(value[1]).
|
||||
"""
|
||||
clean_data = []
|
||||
errors = ErrorList()
|
||||
if self.required and not value:
|
||||
raise ValidationError(gettext(u'This field is required.'))
|
||||
elif not self.required and not value:
|
||||
return self.compress([])
|
||||
if not isinstance(value, (list, tuple)):
|
||||
raise ValidationError(gettext(u'Enter a list of values.'))
|
||||
for i, field in enumerate(self.fields):
|
||||
try:
|
||||
field_value = value[i]
|
||||
except KeyError:
|
||||
field_value = None
|
||||
if self.required and field_value in EMPTY_VALUES:
|
||||
raise ValidationError(gettext(u'This field is required.'))
|
||||
try:
|
||||
clean_data.append(field.clean(field_value))
|
||||
except ValidationError, e:
|
||||
# Collect all validation errors in a single list, which we'll
|
||||
# raise at the end of clean(), rather than raising a single
|
||||
# exception for the first error we encounter.
|
||||
errors.extend(e.messages)
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
return self.compress(clean_data)
|
||||
|
||||
def compress(self, data_list):
|
||||
"""
|
||||
Returns a single value for the given list of values. The values can be
|
||||
assumed to be valid.
|
||||
|
||||
For example, if this MultiValueField was instantiated with
|
||||
fields=(DateField(), TimeField()), this might return a datetime
|
||||
object created by combining the date and time in data_list.
|
||||
"""
|
||||
raise NotImplementedError('Subclasses must implement this method.')
|
||||
|
||||
class SplitDateTimeField(MultiValueField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
fields = (DateField(), TimeField())
|
||||
super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
|
||||
|
||||
def compress(self, data_list):
|
||||
if data_list:
|
||||
return datetime.datetime.combine(*data_list)
|
||||
return None
|
||||
|
@ -5,8 +5,8 @@ Form classes
|
||||
from django.utils.datastructures import SortedDict, MultiValueDict
|
||||
from django.utils.html import escape
|
||||
from fields import Field
|
||||
from widgets import TextInput, Textarea, HiddenInput
|
||||
from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError
|
||||
from widgets import TextInput, Textarea, HiddenInput, MultipleHiddenInput
|
||||
from util import flatatt, StrAndUnicode, ErrorDict, ErrorList, ValidationError
|
||||
|
||||
__all__ = ('BaseForm', 'Form')
|
||||
|
||||
@ -26,12 +26,15 @@ class SortedDictFromList(SortedDict):
|
||||
self.keyOrder = [d[0] for d in data]
|
||||
dict.__init__(self, dict(data))
|
||||
|
||||
def copy(self):
|
||||
return SortedDictFromList(self.items())
|
||||
|
||||
class DeclarativeFieldsMetaclass(type):
|
||||
"Metaclass that converts Field attributes to a dictionary called 'fields'."
|
||||
"Metaclass that converts Field attributes to a dictionary called 'base_fields'."
|
||||
def __new__(cls, name, bases, attrs):
|
||||
fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)]
|
||||
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
|
||||
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
|
||||
attrs['fields'] = SortedDictFromList(fields)
|
||||
attrs['base_fields'] = SortedDictFromList(fields)
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
|
||||
class BaseForm(StrAndUnicode):
|
||||
@ -39,13 +42,19 @@ class BaseForm(StrAndUnicode):
|
||||
# class is different than Form. See the comments by the Form class for more
|
||||
# information. Any improvements to the form API should be made to *this*
|
||||
# class, not to the Form class.
|
||||
def __init__(self, data=None, auto_id='id_%s', prefix=None):
|
||||
self.ignore_errors = data is None
|
||||
def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):
|
||||
self.is_bound = data is not None
|
||||
self.data = data or {}
|
||||
self.auto_id = auto_id
|
||||
self.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.
|
||||
# The base_fields class attribute is the *class-wide* definition of
|
||||
# fields. Because a particular *instance* of the class might want to
|
||||
# alter self.fields, we create self.fields here by copying base_fields.
|
||||
# Instances should always modify self.fields; they should not modify
|
||||
# self.base_fields.
|
||||
self.fields = self.base_fields.copy()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.as_table()
|
||||
@ -74,7 +83,7 @@ class BaseForm(StrAndUnicode):
|
||||
Returns True if the form has no errors. Otherwise, False. If errors are
|
||||
being ignored, returns False.
|
||||
"""
|
||||
return not self.ignore_errors and not bool(self.errors)
|
||||
return self.is_bound and not bool(self.errors)
|
||||
|
||||
def 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
|
||||
|
||||
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()."
|
||||
top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
|
||||
output, hidden_fields = [], []
|
||||
@ -100,7 +109,11 @@ class BaseForm(StrAndUnicode):
|
||||
if errors_on_separate_row and bf_errors:
|
||||
output.append(error_row % bf_errors)
|
||||
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:
|
||||
output.insert(0, error_row % top_errors)
|
||||
if hidden_fields: # Insert any hidden fields in the last row.
|
||||
@ -115,15 +128,15 @@ class BaseForm(StrAndUnicode):
|
||||
|
||||
def as_table(self):
|
||||
"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):
|
||||
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
|
||||
return self._html_output(u'<li>%(errors)s%(label)s %(field)s</li>', u'<li>%s</li>', '</li>', False)
|
||||
return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
|
||||
|
||||
def as_p(self):
|
||||
"Returns this form rendered as HTML <p>s."
|
||||
return self._html_output(u'<p>%(label)s %(field)s</p>', u'<p>%s</p>', '</p>', True)
|
||||
return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'<p>%s</p>', '</p>', u' %s', True)
|
||||
|
||||
def non_field_errors(self):
|
||||
"""
|
||||
@ -137,11 +150,11 @@ class BaseForm(StrAndUnicode):
|
||||
"""
|
||||
Cleans all of self.data and populates self.__errors and self.clean_data.
|
||||
"""
|
||||
self.clean_data = {}
|
||||
errors = ErrorDict()
|
||||
if self.ignore_errors: # Stop further processing.
|
||||
if not self.is_bound: # Stop further processing.
|
||||
self.__errors = errors
|
||||
return
|
||||
self.clean_data = {}
|
||||
for name, field in self.fields.items():
|
||||
# value_from_datadict() gets the data from the dictionary.
|
||||
# Each widget type knows how to retrieve its own data, because some
|
||||
@ -160,7 +173,7 @@ class BaseForm(StrAndUnicode):
|
||||
except ValidationError, e:
|
||||
errors[NON_FIELD_ERRORS] = e.messages
|
||||
if errors:
|
||||
self.clean_data = None
|
||||
delattr(self, 'clean_data')
|
||||
self.__errors = errors
|
||||
|
||||
def clean(self):
|
||||
@ -218,8 +231,8 @@ class BoundField(StrAndUnicode):
|
||||
auto_id = self.auto_id
|
||||
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
|
||||
attrs['id'] = auto_id
|
||||
if self.form.ignore_errors:
|
||||
data = self.field.initial
|
||||
if not self.form.is_bound:
|
||||
data = self.form.initial.get(self.name, self.field.initial)
|
||||
else:
|
||||
data = self.data
|
||||
return widget.render(self.html_name, data, attrs=attrs)
|
||||
@ -238,7 +251,7 @@ class BoundField(StrAndUnicode):
|
||||
"""
|
||||
Returns a string of HTML for representing this as an <input type="hidden">.
|
||||
"""
|
||||
return self.as_widget(HiddenInput(), attrs)
|
||||
return self.as_widget(self.field.hidden_widget(), attrs)
|
||||
|
||||
def _data(self):
|
||||
"""
|
||||
@ -247,17 +260,20 @@ class BoundField(StrAndUnicode):
|
||||
return self.field.widget.value_from_datadict(self.form.data, self.html_name)
|
||||
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.
|
||||
Does not HTML-escape the contents. If contents aren't given, uses the
|
||||
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)
|
||||
widget = self.field.widget
|
||||
id_ = widget.attrs.get('id') or self.auto_id
|
||||
if id_:
|
||||
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents)
|
||||
attrs = attrs and flatatt(attrs) or ''
|
||||
contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents)
|
||||
return contents
|
||||
|
||||
def _is_hidden(self):
|
||||
|
@ -5,9 +5,9 @@ and database field objects.
|
||||
|
||||
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.
|
||||
|
||||
@ -15,61 +15,84 @@ def create(self, save=True):
|
||||
"""
|
||||
if self.errors:
|
||||
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
|
||||
obj = self._model(**self.clean_data)
|
||||
if save:
|
||||
obj.save()
|
||||
return obj
|
||||
return save_instance(self, self._model(), commit)
|
||||
|
||||
def make_apply_changes(opts, instance):
|
||||
"Returns the apply_changes() method for a form_for_instance Form."
|
||||
def save_instance(form, instance, commit=True):
|
||||
"""
|
||||
Saves bound Form ``form``'s clean_data into model instance ``instance``.
|
||||
|
||||
Assumes ``form`` has a field for every non-AutoField database field in
|
||||
``instance``. If commit=True, then the changes to ``instance`` will be
|
||||
saved to the database. Returns ``instance``.
|
||||
"""
|
||||
from django.db import models
|
||||
def apply_changes(self, save=True):
|
||||
if self.errors:
|
||||
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
||||
clean_data = self.clean_data
|
||||
for f in opts.fields + opts.many_to_many:
|
||||
if isinstance(f, models.AutoField):
|
||||
continue
|
||||
opts = instance.__class__._meta
|
||||
if form.errors:
|
||||
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
||||
clean_data = form.clean_data
|
||||
for f in opts.fields:
|
||||
if isinstance(f, models.AutoField):
|
||||
continue
|
||||
setattr(instance, f.attname, clean_data[f.name])
|
||||
if commit:
|
||||
instance.save()
|
||||
for f in opts.many_to_many:
|
||||
setattr(instance, f.attname, clean_data[f.name])
|
||||
if save:
|
||||
instance.save()
|
||||
return instance
|
||||
return apply_changes
|
||||
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
|
||||
# data will be lost. This happens because a many-to-many options cannot be
|
||||
# set on an object until after it's saved. Maybe we should raise an
|
||||
# exception in that case.
|
||||
return instance
|
||||
|
||||
def 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.
|
||||
|
||||
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
|
||||
field_list = []
|
||||
for f in opts.fields + opts.many_to_many:
|
||||
formfield = f.formfield()
|
||||
formfield = formfield_callback(f)
|
||||
if formfield:
|
||||
field_list.append((f.name, formfield))
|
||||
fields = SortedDictFromList(field_list)
|
||||
return type(opts.object_name + 'Form', (form,), {'fields': fields, '_model': model, 'create': create})
|
||||
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.
|
||||
|
||||
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__
|
||||
opts = model._meta
|
||||
field_list = []
|
||||
for f in opts.fields + opts.many_to_many:
|
||||
current_value = f.value_from_object(instance)
|
||||
formfield = f.formfield(initial=current_value)
|
||||
formfield = formfield_callback(f, initial=current_value)
|
||||
if formfield:
|
||||
field_list.append((f.name, formfield))
|
||||
fields = SortedDictFromList(field_list)
|
||||
return type(opts.object_name + 'InstanceForm', (form,),
|
||||
{'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):
|
||||
"Returns a Form class for the given list of Django database field instances."
|
||||
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
|
||||
return type('FormForFields', (BaseForm,), {'fields': fields})
|
||||
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
||||
|
@ -1,4 +1,9 @@
|
||||
from django.conf import settings
|
||||
from django.utils.html import escape
|
||||
|
||||
# Converts a dictionary to a single string with key="value", XML-style with
|
||||
# a leading space. Assumes keys do not need to be XML-escaped.
|
||||
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
||||
|
||||
def smart_unicode(s):
|
||||
if not isinstance(s, basestring):
|
||||
|
@ -3,14 +3,16 @@ HTML Widget classes
|
||||
"""
|
||||
|
||||
__all__ = (
|
||||
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
|
||||
'Textarea', 'CheckboxInput',
|
||||
'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
|
||||
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',
|
||||
'FileInput', 'Textarea', 'CheckboxInput',
|
||||
'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
|
||||
'MultiWidget', 'SplitDateTimeWidget',
|
||||
)
|
||||
|
||||
from util import StrAndUnicode, smart_unicode
|
||||
from util import flatatt, StrAndUnicode, smart_unicode
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import gettext
|
||||
from itertools import chain
|
||||
|
||||
try:
|
||||
@ -18,10 +20,6 @@ try:
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
# Converts a dictionary to a single string with key="value", XML-style with
|
||||
# a leading space. Assumes keys do not need to be XML-escaped.
|
||||
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
||||
|
||||
class Widget(object):
|
||||
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
||||
|
||||
@ -87,6 +85,26 @@ class HiddenInput(Input):
|
||||
input_type = 'hidden'
|
||||
is_hidden = True
|
||||
|
||||
class MultipleHiddenInput(HiddenInput):
|
||||
"""
|
||||
A widget that handles <input type="hidden"> for fields that have a list
|
||||
of values.
|
||||
"""
|
||||
def __init__(self, attrs=None, choices=()):
|
||||
# choices can be any iterable
|
||||
self.attrs = attrs or {}
|
||||
self.choices = choices
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
if value is None: value = []
|
||||
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
||||
return u'\n'.join([(u'<input%s />' % flatatt(dict(value=smart_unicode(v), **final_attrs))) for v in value])
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
if isinstance(data, MultiValueDict):
|
||||
return data.getlist(name)
|
||||
return data.get(name, None)
|
||||
|
||||
class FileInput(Input):
|
||||
input_type = 'file'
|
||||
|
||||
@ -118,9 +136,11 @@ class CheckboxInput(Widget):
|
||||
|
||||
class Select(Widget):
|
||||
def __init__(self, attrs=None, choices=()):
|
||||
# choices can be any iterable
|
||||
self.attrs = attrs or {}
|
||||
self.choices = choices
|
||||
# choices can be any iterable, but we may need to render this widget
|
||||
# multiple times. Thus, collapse it into a list so it can be consumed
|
||||
# more than once.
|
||||
self.choices = list(choices)
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
if value is None: value = ''
|
||||
@ -134,6 +154,25 @@ class Select(Widget):
|
||||
output.append(u'</select>')
|
||||
return u'\n'.join(output)
|
||||
|
||||
class NullBooleanSelect(Select):
|
||||
"""
|
||||
A Select Widget intended to be used with NullBooleanField.
|
||||
"""
|
||||
def __init__(self, attrs=None):
|
||||
choices = ((u'1', gettext('Unknown')), (u'2', gettext('Yes')), (u'3', gettext('No')))
|
||||
super(NullBooleanSelect, self).__init__(attrs, choices)
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
try:
|
||||
value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value]
|
||||
except KeyError:
|
||||
value = u'1'
|
||||
return super(NullBooleanSelect, self).render(name, value, attrs, choices)
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
value = data.get(name, None)
|
||||
return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
|
||||
|
||||
class SelectMultiple(Widget):
|
||||
def __init__(self, attrs=None, choices=()):
|
||||
# choices can be any iterable
|
||||
@ -162,14 +201,15 @@ class RadioInput(StrAndUnicode):
|
||||
def __init__(self, name, value, attrs, choice, index):
|
||||
self.name, self.value = name, value
|
||||
self.attrs = attrs
|
||||
self.choice_value, self.choice_label = choice
|
||||
self.choice_value = smart_unicode(choice[0])
|
||||
self.choice_label = smart_unicode(choice[1])
|
||||
self.index = index
|
||||
|
||||
def __unicode__(self):
|
||||
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
||||
|
||||
def is_checked(self):
|
||||
return self.value == smart_unicode(self.choice_value)
|
||||
return self.value == self.choice_value
|
||||
|
||||
def tag(self):
|
||||
if self.attrs.has_key('id'):
|
||||
@ -218,11 +258,16 @@ class RadioSelect(Select):
|
||||
class CheckboxSelectMultiple(SelectMultiple):
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
if value is None: value = []
|
||||
has_id = attrs and attrs.has_key('id')
|
||||
final_attrs = self.build_attrs(attrs, name=name)
|
||||
output = [u'<ul>']
|
||||
str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
|
||||
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
|
||||
for option_value, option_label in chain(self.choices, choices):
|
||||
for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
|
||||
# If an ID attribute was given, add a numeric index as a suffix,
|
||||
# so that the checkboxes don't all have the same ID attribute.
|
||||
if has_id:
|
||||
final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
|
||||
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
|
||||
option_value = smart_unicode(option_value)
|
||||
rendered_cb = cb.render(name, option_value)
|
||||
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label))))
|
||||
@ -235,3 +280,66 @@ class CheckboxSelectMultiple(SelectMultiple):
|
||||
id_ += '_0'
|
||||
return id_
|
||||
id_for_label = classmethod(id_for_label)
|
||||
|
||||
class MultiWidget(Widget):
|
||||
"""
|
||||
A widget that is composed of multiple widgets.
|
||||
|
||||
Its render() method takes a "decompressed" list of values, not a single
|
||||
value. Each value in this list is rendered in the corresponding widget --
|
||||
the first value is rendered in the first widget, the second value is
|
||||
rendered in the second widget, etc.
|
||||
|
||||
Subclasses should implement decompress(), which specifies how a single
|
||||
value should be converted to a list of values. Subclasses should not
|
||||
have to implement clean().
|
||||
|
||||
Subclasses may implement format_output(), which takes the list of rendered
|
||||
widgets and returns HTML that formats them any way you'd like.
|
||||
|
||||
You'll probably want to use this with MultiValueField.
|
||||
"""
|
||||
def __init__(self, widgets, attrs=None):
|
||||
self.widgets = [isinstance(w, type) and w() or w for w in widgets]
|
||||
super(MultiWidget, self).__init__(attrs)
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
# value is a list of values, each corresponding to a widget
|
||||
# in self.widgets.
|
||||
if not isinstance(value, list):
|
||||
value = self.decompress(value)
|
||||
output = []
|
||||
for i, widget in enumerate(self.widgets):
|
||||
try:
|
||||
widget_value = value[i]
|
||||
except KeyError:
|
||||
widget_value = None
|
||||
output.append(widget.render(name + '_%s' % i, widget_value, attrs))
|
||||
return self.format_output(output)
|
||||
|
||||
def value_from_datadict(self, data, name):
|
||||
return [data.get(name + '_%s' % i) for i in range(len(self.widgets))]
|
||||
|
||||
def format_output(self, rendered_widgets):
|
||||
return u''.join(rendered_widgets)
|
||||
|
||||
def decompress(self, value):
|
||||
"""
|
||||
Returns a list of decompressed values for the given compressed value.
|
||||
The given value can be assumed to be valid, but not necessarily
|
||||
non-empty.
|
||||
"""
|
||||
raise NotImplementedError('Subclasses must implement this method.')
|
||||
|
||||
class SplitDateTimeWidget(MultiWidget):
|
||||
"""
|
||||
A Widget that splits datetime input into two <input type="text"> boxes.
|
||||
"""
|
||||
def __init__(self, attrs=None):
|
||||
widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs))
|
||||
super(SplitDateTimeWidget, self).__init__(widgets, attrs)
|
||||
|
||||
def decompress(self, value):
|
||||
if value:
|
||||
return [value.date(), value.time()]
|
||||
return [None, None]
|
||||
|
@ -569,7 +569,7 @@ class NullBooleanField(SelectField):
|
||||
"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):
|
||||
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)
|
||||
|
||||
def render(self, data):
|
||||
@ -958,7 +958,9 @@ class USStateField(TextField):
|
||||
raise validators.CriticalValidationError, e.messages
|
||||
|
||||
def html2python(data):
|
||||
return data.upper() # Should always be stored in upper case
|
||||
if data:
|
||||
return data.upper() # Should always be stored in upper case
|
||||
return data
|
||||
html2python = staticmethod(html2python)
|
||||
|
||||
class CommaSeparatedIntegerField(TextField):
|
||||
|
@ -70,7 +70,7 @@ class SortedDict(dict):
|
||||
return self.keyOrder[:]
|
||||
|
||||
def values(self):
|
||||
return [dict.__getitem__(self,k) for k in self.keyOrder]
|
||||
return [dict.__getitem__(self, k) for k in self.keyOrder]
|
||||
|
||||
def update(self, dict):
|
||||
for k, v in dict.items():
|
||||
@ -81,6 +81,10 @@ class SortedDict(dict):
|
||||
self.keyOrder.append(key)
|
||||
return dict.setdefault(self, key, default)
|
||||
|
||||
def value_for_index(self, index):
|
||||
"Returns the value of the item at the given zero-based index."
|
||||
return self[self.keyOrder[index]]
|
||||
|
||||
class MultiValueDictKeyError(KeyError):
|
||||
pass
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
simplejson 1.3
|
||||
simplejson 1.5
|
||||
Copyright (c) 2006 Bob Ippolito
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
|
@ -27,6 +27,21 @@ Encoding basic Python object hierarchies::
|
||||
>>> io.getvalue()
|
||||
'["streaming API"]'
|
||||
|
||||
Compact encoding::
|
||||
|
||||
>>> import simplejson
|
||||
>>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
|
||||
'[1,2,3,{"4":5,"6":7}]'
|
||||
|
||||
Pretty printing::
|
||||
|
||||
>>> import simplejson
|
||||
>>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
|
||||
{
|
||||
"4": 5,
|
||||
"6": 7
|
||||
}
|
||||
|
||||
Decoding JSON::
|
||||
|
||||
>>> import simplejson
|
||||
@ -68,10 +83,10 @@ Extending JSONEncoder::
|
||||
['[', '2.0', ', ', '1.0', ']']
|
||||
|
||||
|
||||
Note that the JSON produced by this module is a subset of YAML,
|
||||
so it may be used as a serializer for that as well.
|
||||
Note that the JSON produced by this module's default settings
|
||||
is a subset of YAML, so it may be used as a serializer for that as well.
|
||||
"""
|
||||
__version__ = '1.3'
|
||||
__version__ = '1.5'
|
||||
__all__ = [
|
||||
'dump', 'dumps', 'load', 'loads',
|
||||
'JSONDecoder', 'JSONEncoder',
|
||||
@ -81,7 +96,7 @@ from django.utils.simplejson.decoder import JSONDecoder
|
||||
from django.utils.simplejson.encoder import JSONEncoder
|
||||
|
||||
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
allow_nan=True, cls=None, **kw):
|
||||
allow_nan=True, cls=None, indent=None, **kw):
|
||||
"""
|
||||
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
|
||||
``.write()``-supporting file-like object).
|
||||
@ -105,6 +120,10 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
in strict compliance of the JSON specification, instead of using the
|
||||
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
||||
|
||||
If ``indent`` is a non-negative integer, then JSON array elements and object
|
||||
members will be pretty-printed with that indent level. An indent level
|
||||
of 0 will only insert newlines. ``None`` is the most compact representation.
|
||||
|
||||
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
||||
``.default()`` method to serialize additional types), specify it with
|
||||
the ``cls`` kwarg.
|
||||
@ -112,7 +131,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
if cls is None:
|
||||
cls = JSONEncoder
|
||||
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||
check_circular=check_circular, allow_nan=allow_nan,
|
||||
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
|
||||
**kw).iterencode(obj)
|
||||
# could accelerate with writelines in some versions of Python, at
|
||||
# a debuggability cost
|
||||
@ -120,7 +139,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
fp.write(chunk)
|
||||
|
||||
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
allow_nan=True, cls=None, **kw):
|
||||
allow_nan=True, cls=None, indent=None, separators=None, **kw):
|
||||
"""
|
||||
Serialize ``obj`` to a JSON formatted ``str``.
|
||||
|
||||
@ -141,14 +160,26 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||
strict compliance of the JSON specification, instead of using the
|
||||
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
||||
|
||||
If ``indent`` is a non-negative integer, then JSON array elements and
|
||||
object members will be pretty-printed with that indent level. An indent
|
||||
level of 0 will only insert newlines. ``None`` is the most compact
|
||||
representation.
|
||||
|
||||
If ``separators`` is an ``(item_separator, dict_separator)`` tuple
|
||||
then it will be used instead of the default ``(', ', ': ')`` separators.
|
||||
``(',', ':')`` is the most compact JSON representation.
|
||||
|
||||
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
||||
``.default()`` method to serialize additional types), specify it with
|
||||
the ``cls`` kwarg.
|
||||
"""
|
||||
if cls is None:
|
||||
cls = JSONEncoder
|
||||
return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||
check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj)
|
||||
return cls(
|
||||
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
|
||||
separators=separators,
|
||||
**kw).encode(obj)
|
||||
|
||||
def load(fp, encoding=None, cls=None, object_hook=None, **kw):
|
||||
"""
|
||||
|
@ -127,6 +127,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
|
||||
raise ValueError(errmsg("Expecting property name", s, end))
|
||||
end += 1
|
||||
encoding = getattr(context, 'encoding', None)
|
||||
iterscan = JSONScanner.iterscan
|
||||
while True:
|
||||
key, end = scanstring(s, end, encoding)
|
||||
end = _w(s, end).end()
|
||||
@ -134,7 +135,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
|
||||
raise ValueError(errmsg("Expecting : delimiter", s, end))
|
||||
end = _w(s, end + 1).end()
|
||||
try:
|
||||
value, end = JSONScanner.iterscan(s, idx=end).next()
|
||||
value, end = iterscan(s, idx=end, context=context).next()
|
||||
except StopIteration:
|
||||
raise ValueError(errmsg("Expecting object", s, end))
|
||||
pairs[key] = value
|
||||
@ -164,9 +165,10 @@ def JSONArray(match, context, _w=WHITESPACE.match):
|
||||
nextchar = s[end:end + 1]
|
||||
if nextchar == ']':
|
||||
return values, end + 1
|
||||
iterscan = JSONScanner.iterscan
|
||||
while True:
|
||||
try:
|
||||
value, end = JSONScanner.iterscan(s, idx=end).next()
|
||||
value, end = iterscan(s, idx=end, context=context).next()
|
||||
except StopIteration:
|
||||
raise ValueError(errmsg("Expecting object", s, end))
|
||||
values.append(value)
|
||||
|
@ -3,11 +3,11 @@ Implementation of JSONEncoder
|
||||
"""
|
||||
import re
|
||||
|
||||
# this should match any kind of infinity
|
||||
INFCHARS = re.compile(r'[infINF]')
|
||||
ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
|
||||
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
|
||||
ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
|
||||
ESCAPE_DCT = {
|
||||
# escape all forward slashes to prevent </script> attack
|
||||
'/': '\\/',
|
||||
'\\': '\\\\',
|
||||
'"': '\\"',
|
||||
'\b': '\\b',
|
||||
@ -16,31 +16,31 @@ ESCAPE_DCT = {
|
||||
'\r': '\\r',
|
||||
'\t': '\\t',
|
||||
}
|
||||
for i in range(20):
|
||||
for i in range(0x20):
|
||||
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
|
||||
|
||||
# assume this produces an infinity on all machines (probably not guaranteed)
|
||||
INFINITY = float('1e66666')
|
||||
|
||||
def floatstr(o, allow_nan=True):
|
||||
s = str(o)
|
||||
# If the first non-sign is a digit then it's not a special value
|
||||
if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
|
||||
return s
|
||||
elif not allow_nan:
|
||||
# Check for specials. Note that this type of test is processor- and/or
|
||||
# platform-specific, so do tests which don't depend on the internals.
|
||||
|
||||
if o != o:
|
||||
text = 'NaN'
|
||||
elif o == INFINITY:
|
||||
text = 'Infinity'
|
||||
elif o == -INFINITY:
|
||||
text = '-Infinity'
|
||||
else:
|
||||
return str(o)
|
||||
|
||||
if not allow_nan:
|
||||
raise ValueError("Out of range float values are not JSON compliant: %r"
|
||||
% (o,))
|
||||
# These are the string representations on the platforms I've tried
|
||||
if s == 'nan':
|
||||
return 'NaN'
|
||||
if s == 'inf':
|
||||
return 'Infinity'
|
||||
if s == '-inf':
|
||||
return '-Infinity'
|
||||
# NaN should either be inequal to itself, or equal to everything
|
||||
if o != o or o == 0.0:
|
||||
return 'NaN'
|
||||
# Last ditch effort, assume inf
|
||||
if o < 0:
|
||||
return '-Infinity'
|
||||
return 'Infinity'
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def encode_basestring(s):
|
||||
"""
|
||||
@ -90,8 +90,11 @@ class JSONEncoder(object):
|
||||
implementation (to raise ``TypeError``).
|
||||
"""
|
||||
__all__ = ['__init__', 'default', 'encode', 'iterencode']
|
||||
item_separator = ', '
|
||||
key_separator = ': '
|
||||
def __init__(self, skipkeys=False, ensure_ascii=True,
|
||||
check_circular=True, allow_nan=True, sort_keys=False):
|
||||
check_circular=True, allow_nan=True, sort_keys=False,
|
||||
indent=None, separators=None):
|
||||
"""
|
||||
Constructor for JSONEncoder, with sensible defaults.
|
||||
|
||||
@ -116,6 +119,15 @@ class JSONEncoder(object):
|
||||
If sort_keys is True, then the output of dictionaries will be
|
||||
sorted by key; this is useful for regression tests to ensure
|
||||
that JSON serializations can be compared on a day-to-day basis.
|
||||
|
||||
If indent is a non-negative integer, then JSON array
|
||||
elements and object members will be pretty-printed with that
|
||||
indent level. An indent level of 0 will only insert newlines.
|
||||
None is the most compact representation.
|
||||
|
||||
If specified, separators should be a (item_separator, key_separator)
|
||||
tuple. The default is (', ', ': '). To get the most compact JSON
|
||||
representation you should specify (',', ':') to eliminate whitespace.
|
||||
"""
|
||||
|
||||
self.skipkeys = skipkeys
|
||||
@ -123,6 +135,13 @@ class JSONEncoder(object):
|
||||
self.check_circular = check_circular
|
||||
self.allow_nan = allow_nan
|
||||
self.sort_keys = sort_keys
|
||||
self.indent = indent
|
||||
self.current_indent_level = 0
|
||||
if separators is not None:
|
||||
self.item_separator, self.key_separator = separators
|
||||
|
||||
def _newline_indent(self):
|
||||
return '\n' + (' ' * (self.indent * self.current_indent_level))
|
||||
|
||||
def _iterencode_list(self, lst, markers=None):
|
||||
if not lst:
|
||||
@ -134,14 +153,25 @@ class JSONEncoder(object):
|
||||
raise ValueError("Circular reference detected")
|
||||
markers[markerid] = lst
|
||||
yield '['
|
||||
if self.indent is not None:
|
||||
self.current_indent_level += 1
|
||||
newline_indent = self._newline_indent()
|
||||
separator = self.item_separator + newline_indent
|
||||
yield newline_indent
|
||||
else:
|
||||
newline_indent = None
|
||||
separator = self.item_separator
|
||||
first = True
|
||||
for value in lst:
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
yield ', '
|
||||
yield separator
|
||||
for chunk in self._iterencode(value, markers):
|
||||
yield chunk
|
||||
if newline_indent is not None:
|
||||
self.current_indent_level -= 1
|
||||
yield self._newline_indent()
|
||||
yield ']'
|
||||
if markers is not None:
|
||||
del markers[markerid]
|
||||
@ -156,6 +186,15 @@ class JSONEncoder(object):
|
||||
raise ValueError("Circular reference detected")
|
||||
markers[markerid] = dct
|
||||
yield '{'
|
||||
key_separator = self.key_separator
|
||||
if self.indent is not None:
|
||||
self.current_indent_level += 1
|
||||
newline_indent = self._newline_indent()
|
||||
item_separator = self.item_separator + newline_indent
|
||||
yield newline_indent
|
||||
else:
|
||||
newline_indent = None
|
||||
item_separator = self.item_separator
|
||||
first = True
|
||||
if self.ensure_ascii:
|
||||
encoder = encode_basestring_ascii
|
||||
@ -165,7 +204,7 @@ class JSONEncoder(object):
|
||||
if self.sort_keys:
|
||||
keys = dct.keys()
|
||||
keys.sort()
|
||||
items = [(k,dct[k]) for k in keys]
|
||||
items = [(k, dct[k]) for k in keys]
|
||||
else:
|
||||
items = dct.iteritems()
|
||||
for key, value in items:
|
||||
@ -190,11 +229,14 @@ class JSONEncoder(object):
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
yield ', '
|
||||
yield item_separator
|
||||
yield encoder(key)
|
||||
yield ': '
|
||||
yield key_separator
|
||||
for chunk in self._iterencode(value, markers):
|
||||
yield chunk
|
||||
if newline_indent is not None:
|
||||
self.current_indent_level -= 1
|
||||
yield self._newline_indent()
|
||||
yield '}'
|
||||
if markers is not None:
|
||||
del markers[markerid]
|
||||
|
40
django/utils/simplejson/jsonfilter.py
Normal file
40
django/utils/simplejson/jsonfilter.py
Normal file
@ -0,0 +1,40 @@
|
||||
from django.utils import simplejson
|
||||
import cgi
|
||||
|
||||
class JSONFilter(object):
|
||||
def __init__(self, app, mime_type='text/x-json'):
|
||||
self.app = app
|
||||
self.mime_type = mime_type
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
# Read JSON POST input to jsonfilter.json if matching mime type
|
||||
response = {'status': '200 OK', 'headers': []}
|
||||
def json_start_response(status, headers):
|
||||
response['status'] = status
|
||||
response['headers'].extend(headers)
|
||||
environ['jsonfilter.mime_type'] = self.mime_type
|
||||
if environ.get('REQUEST_METHOD', '') == 'POST':
|
||||
if environ.get('CONTENT_TYPE', '') == self.mime_type:
|
||||
args = [_ for _ in [environ.get('CONTENT_LENGTH')] if _]
|
||||
data = environ['wsgi.input'].read(*map(int, args))
|
||||
environ['jsonfilter.json'] = simplejson.loads(data)
|
||||
res = simplejson.dumps(self.app(environ, json_start_response))
|
||||
jsonp = cgi.parse_qs(environ.get('QUERY_STRING', '')).get('jsonp')
|
||||
if jsonp:
|
||||
content_type = 'text/javascript'
|
||||
res = ''.join(jsonp + ['(', res, ')'])
|
||||
elif 'Opera' in environ.get('HTTP_USER_AGENT', ''):
|
||||
# Opera has bunk XMLHttpRequest support for most mime types
|
||||
content_type = 'text/plain'
|
||||
else:
|
||||
content_type = self.mime_type
|
||||
headers = [
|
||||
('Content-type', content_type),
|
||||
('Content-length', len(res)),
|
||||
]
|
||||
headers.extend(response['headers'])
|
||||
start_response(response['status'], headers)
|
||||
return [res]
|
||||
|
||||
def factory(app, global_conf, **kw):
|
||||
return JSONFilter(app, **kw)
|
@ -3,11 +3,12 @@ Iterator based sre token scanner
|
||||
"""
|
||||
import sre_parse, sre_compile, sre_constants
|
||||
from sre_constants import BRANCH, SUBPATTERN
|
||||
from re import VERBOSE, MULTILINE, DOTALL
|
||||
import re
|
||||
|
||||
__all__ = ['Scanner', 'pattern']
|
||||
|
||||
FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL)
|
||||
FLAGS = (VERBOSE | MULTILINE | DOTALL)
|
||||
class Scanner(object):
|
||||
def __init__(self, lexicon, flags=FLAGS):
|
||||
self.actions = [None]
|
||||
|
@ -17,7 +17,7 @@ admin
|
||||
The automatic Django administrative interface. For more information, see
|
||||
`Tutorial 2`_.
|
||||
|
||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
||||
.. _Tutorial 2: ../tutorial2/
|
||||
|
||||
auth
|
||||
====
|
||||
@ -26,7 +26,7 @@ Django's authentication framework.
|
||||
|
||||
See the `authentication documentation`_.
|
||||
|
||||
.. _authentication documentation: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _authentication documentation: ../authentication/
|
||||
|
||||
comments
|
||||
========
|
||||
@ -46,7 +46,7 @@ A middleware for preventing Cross Site Request Forgeries
|
||||
|
||||
See the `csrf documentation`_.
|
||||
|
||||
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/
|
||||
.. _csrf documentation: ../csrf/
|
||||
|
||||
formtools
|
||||
=========
|
||||
@ -137,7 +137,7 @@ A framework for managing simple "flat" HTML content in a database.
|
||||
|
||||
See the `flatpages documentation`_.
|
||||
|
||||
.. _flatpages documentation: http://www.djangoproject.com/documentation/flatpages/
|
||||
.. _flatpages documentation: ../flatpages/
|
||||
|
||||
markup
|
||||
======
|
||||
@ -157,7 +157,7 @@ A framework for managing redirects.
|
||||
|
||||
See the `redirects documentation`_.
|
||||
|
||||
.. _redirects documentation: http://www.djangoproject.com/documentation/redirects/
|
||||
.. _redirects documentation: ../redirects/
|
||||
|
||||
sites
|
||||
=====
|
||||
@ -168,7 +168,7 @@ one or more sites.
|
||||
|
||||
See the `sites documentation`_.
|
||||
|
||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
||||
.. _sites documentation: ../sites/
|
||||
|
||||
sitemaps
|
||||
========
|
||||
@ -177,7 +177,7 @@ A framework for generating Google sitemap XML files.
|
||||
|
||||
See the `sitemaps documentation`_.
|
||||
|
||||
.. _sitemaps documentation: http://www.djangoproject.com/documentation/sitemaps/
|
||||
.. _sitemaps documentation: ../sitemaps/
|
||||
|
||||
syndication
|
||||
===========
|
||||
@ -186,7 +186,7 @@ A framework for generating syndication feeds, in RSS and Atom, quite easily.
|
||||
|
||||
See the `syndication documentation`_.
|
||||
|
||||
.. _syndication documentation: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _syndication documentation: ../syndication/
|
||||
|
||||
Other add-ons
|
||||
=============
|
||||
|
@ -65,7 +65,7 @@ are equivalent::
|
||||
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
|
||||
PythonOption DJANGO_SETTINGS_MODULE mysite.settings
|
||||
|
||||
.. _authentication system: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _authentication system: ../authentication/
|
||||
.. _Subversion: http://subversion.tigris.org/
|
||||
.. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html
|
||||
.. _custom permissions: http://www.djangoproject.com/documentation/authentication/#custom-permissions
|
||||
.. _custom permissions: ../authentication/#custom-permissions
|
||||
|
@ -98,26 +98,26 @@ change:
|
||||
rewrite before Django 1.0. Even if the change isn't quite that drastic,
|
||||
there will at least be moderate changes.
|
||||
|
||||
.. _caching: http://www.djangoproject.com/documentation/cache/
|
||||
.. _custom template tags and libraries: http://www.djangoproject.com/documentation/templates_python/
|
||||
.. _database lookup: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _django-admin utility: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _fastcgi integration: http://www.djangoproject.com/documentation/fastcgi/
|
||||
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
|
||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _internationalization: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _legacy database integration: http://www.djangoproject.com/documentation/legacy_databases/
|
||||
.. _model definition: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _mod_python integration: http://www.djangoproject.com/documentation/modpython/
|
||||
.. _redirects: http://www.djangoproject.com/documentation/redirects/
|
||||
.. _request/response objects: http://www.djangoproject.com/documentation/request_response/
|
||||
.. _sending email: http://www.djangoproject.com/documentation/email/
|
||||
.. _sessions: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _settings: http://www.djangoproject.com/documentation/settings/
|
||||
.. _syndication: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _template language: http://www.djangoproject.com/documentation/templates/
|
||||
.. _transactions: http://www.djangoproject.com/documentation/transactions/
|
||||
.. _url dispatch: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _forms and validation: http://www.djangoproject.com/documentation/forms/
|
||||
.. _serialization: http://www.djangoproject.com/documentation/serialization/
|
||||
.. _authentication: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _caching: ../cache/
|
||||
.. _custom template tags and libraries: ../templates_python/
|
||||
.. _database lookup: ../db_api/
|
||||
.. _django-admin utility: ../django_admin/
|
||||
.. _fastcgi integration: ../fastcgi/
|
||||
.. _flatpages: ../flatpages/
|
||||
.. _generic views: ../generic_views/
|
||||
.. _internationalization: ../i18n/
|
||||
.. _legacy database integration: ../legacy_databases/
|
||||
.. _model definition: ../model_api/
|
||||
.. _mod_python integration: ../modpython/
|
||||
.. _redirects: ../redirects/
|
||||
.. _request/response objects: ../request_response/
|
||||
.. _sending email: ../email/
|
||||
.. _sessions: ../sessions/
|
||||
.. _settings: ../settings/
|
||||
.. _syndication: ../syndication/
|
||||
.. _template language: ../templates/
|
||||
.. _transactions: ../transactions/
|
||||
.. _url dispatch: ../url_dispatch/
|
||||
.. _forms and validation: ../forms/
|
||||
.. _serialization: ../serialization/
|
||||
.. _authentication: ../authentication/
|
||||
|
@ -144,8 +144,8 @@ custom methods:
|
||||
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
|
||||
doesn't allow profiles.
|
||||
|
||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _DEFAULT_FROM_EMAIL: http://www.djangoproject.com/documentation/settings/#default-from-email
|
||||
.. _Django model: ../model_api/
|
||||
.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
|
||||
|
||||
Manager functions
|
||||
~~~~~~~~~~~~~~~~~
|
||||
@ -271,8 +271,8 @@ previous section). You can tell them apart with ``is_authenticated()``, like so:
|
||||
else:
|
||||
# Do something for anonymous users.
|
||||
|
||||
.. _request objects: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
||||
.. _session documentation: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _request objects: ../request_response/#httprequest-objects
|
||||
.. _session documentation: ../sessions/
|
||||
|
||||
How to log a user in
|
||||
--------------------
|
||||
@ -441,8 +441,8 @@ block::
|
||||
|
||||
{% endblock %}
|
||||
|
||||
.. _forms documentation: http://www.djangoproject.com/documentation/forms/
|
||||
.. _site framework docs: http://www.djangoproject.com/documentation/sites/
|
||||
.. _forms documentation: ../forms/
|
||||
.. _site framework docs: ../sites/
|
||||
|
||||
Limiting access to logged-in users that pass a test
|
||||
---------------------------------------------------
|
||||
@ -544,7 +544,7 @@ For example::
|
||||
def limited_object_detail(*args, **kwargs):
|
||||
return object_detail(*args, **kwargs)
|
||||
|
||||
.. _generic view: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _generic view: ../generic_views/
|
||||
|
||||
Permissions
|
||||
===========
|
||||
@ -606,7 +606,7 @@ This example model creates three custom permissions::
|
||||
The only thing this does is create those extra permissions when you run
|
||||
``syncdb``.
|
||||
|
||||
.. _model Meta attribute: http://www.djangoproject.com/documentation/model_api/#meta-options
|
||||
.. _model Meta attribute: ../model_api/#meta-options
|
||||
|
||||
API reference
|
||||
-------------
|
||||
@ -645,7 +645,7 @@ The currently logged-in user and his/her permissions are made available in the
|
||||
setting contains ``"django.core.context_processors.auth"``, which is default.
|
||||
For more, see the `RequestContext docs`_.
|
||||
|
||||
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-requestcontext
|
||||
.. _RequestContext docs: ../templates_python/#subclassing-context-requestcontext
|
||||
|
||||
Users
|
||||
-----
|
||||
@ -691,7 +691,7 @@ Thus, you can check permissions in template ``{% if %}`` statements::
|
||||
<p>You don't have permission to do anything in the foo app.</p>
|
||||
{% endif %}
|
||||
|
||||
.. _template context: http://www.djangoproject.com/documentation/templates_python/
|
||||
.. _template context: ../templates_python/
|
||||
|
||||
Groups
|
||||
======
|
||||
@ -756,7 +756,7 @@ scenes, so any messages will be deleted even if you don't display them.
|
||||
Finally, note that this messages framework only works with users in the user
|
||||
database. To send messages to anonymous users, use the `session framework`_.
|
||||
|
||||
.. _session framework: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _session framework: ../sessions/
|
||||
|
||||
Other authentication sources
|
||||
============================
|
||||
|
@ -250,7 +250,7 @@ Additionally, ``CacheMiddleware`` automatically sets a few headers in each
|
||||
|
||||
See the `middleware documentation`_ for more on middleware.
|
||||
|
||||
.. _`middleware documentation`: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _`middleware documentation`: ../middleware/
|
||||
|
||||
The per-view cache
|
||||
==================
|
||||
|
@ -122,9 +122,9 @@ Patch style
|
||||
* Name the patch file with a ``.diff`` extension; this will let the ticket
|
||||
tracker apply correct syntax highlighting, which is quite helpful.
|
||||
|
||||
* Put the prefix "[patch] " before the title of your ticket. This will make
|
||||
it obvious that the ticket includes a patch, and it will add the ticket
|
||||
to the `list of tickets with patches`_.
|
||||
* Check the "Has patch" box on the ticket details. This will make it
|
||||
obvious that the ticket includes a patch, and it will add the ticket to
|
||||
the `list of tickets with patches`_.
|
||||
|
||||
* The code required to fix a problem or add a feature is an essential part
|
||||
of a patch, but it is not the only part. A good patch should also include
|
||||
@ -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
|
||||
don't meet all the requirements of a `good patch`_.
|
||||
|
||||
One way to help out is to *triage* bugs that have been reported by other users.
|
||||
Pick an open ticket that is missing some details, and try to replicate the
|
||||
problem. Fill in the missing pieces of the report. If the ticket doesn't have
|
||||
a patch, create one.
|
||||
One way to help out is to *triage* bugs that have been reported by other
|
||||
users. A couple of dedicated volunteers work on this regularly, but more help
|
||||
is always appreciated.
|
||||
|
||||
Once you've completed all the missing details on the ticket and you have a
|
||||
patch with all the required features, e-mail `django-developers`_. Indicate
|
||||
that you have triaged a ticket, and recommend a course of action for dealing
|
||||
with that ticket.
|
||||
Most of the workflow is based around the concept of a ticket's "triage stage".
|
||||
This stage describes where in its lifetime a given ticket is at any time.
|
||||
Along with a handful of flags, this field easily tells us what and who each
|
||||
ticket is waiting on.
|
||||
|
||||
At first, this may require you to be persistent. If you find that your triaged
|
||||
ticket still isn't getting attention, occasional polite requests for eyeballs
|
||||
to look at your ticket may be necessary. However, as you earn a reputation for
|
||||
quality triage work, you should find that it is easier to get the developers'
|
||||
attention.
|
||||
Since a picture is worth a thousand words, let's start there:
|
||||
|
||||
.. image:: http://media.djangoproject.com/img/doc/djangotickets.png
|
||||
:height: 451
|
||||
:width: 590
|
||||
:alt: Django's ticket workflow
|
||||
|
||||
We've got two roles here:
|
||||
|
||||
* Core developers: people with commit access who make the decisions and
|
||||
write the bulk of the code.
|
||||
|
||||
* Ticket triagers: community members who keep track of tickets, making
|
||||
sure the tickets are always categorized correctly.
|
||||
|
||||
Second, note the four triage stages:
|
||||
|
||||
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
|
||||
examine the ticket and move it along.
|
||||
|
||||
2. "Design decision needed" means "this concept requires a design
|
||||
decision," which should be discussed either in the ticket comments or on
|
||||
django-developers.
|
||||
|
||||
3. Once a ticket is ruled to be approved for fixing, it's moved into the
|
||||
"Accepted" stage. This stage is where all the real work gets done.
|
||||
|
||||
4. If a ticket has an associated patch (see below), a triager will review the
|
||||
patch. If the patch is complete, it'll be marked as "ready for checkin" so
|
||||
that a core developer knows to review and check in the patches.
|
||||
|
||||
The second part of this workflow involves a set of flags the describe what the
|
||||
ticket has or needs in order to be "ready for checkin":
|
||||
|
||||
"Has patch"
|
||||
The means the ticket has an associated patch_. These will be
|
||||
reviewed to see if the patch is "good".
|
||||
|
||||
"Needs documentation"
|
||||
This flag is used for tickets with patches that need associated
|
||||
documentation. Complete documentation of features is a prerequisite
|
||||
before we can check a fix into the codebase.
|
||||
|
||||
"Needs tests"
|
||||
This flags the patch as needing associated unit tests. Again, this is a
|
||||
required part of a valid patch.
|
||||
|
||||
"Patch needs improvement"
|
||||
This flag means that although the ticket *has* a patch, it's not quite
|
||||
ready for checkin. This could mean the patch no longer applies
|
||||
cleanly, or that the code doesn't live up to our standards.
|
||||
|
||||
.. _required details: `Reporting bugs`_
|
||||
.. _good patch: `Patch style`_
|
||||
.. _patch: `Submitting patches`_
|
||||
|
||||
Submitting and maintaining translations
|
||||
=======================================
|
||||
@ -186,7 +232,7 @@ translated, here's what to do:
|
||||
`i18n documentation`_.
|
||||
|
||||
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
|
||||
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _i18n documentation: ../i18n/
|
||||
|
||||
Coding style
|
||||
============
|
||||
@ -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
|
||||
how to write new tests.
|
||||
|
||||
.. _Testing Django applications: http://www.djangoproject.com/documentation/testing/
|
||||
.. _Testing Django applications: ../testing/
|
||||
|
||||
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
|
||||
.. _django-users: http://groups.google.com/group/django-users
|
||||
.. _`#django`: irc://irc.freenode.net/django
|
||||
.. _list of tickets with patches: http://code.djangoproject.com/report/12
|
||||
.. _list of tickets with patches: http://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority
|
||||
.. _PEP 8: http://www.python.org/peps/pep-0008.html
|
||||
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n
|
||||
.. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases
|
||||
|
@ -525,6 +525,21 @@ Examples::
|
||||
[datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)]
|
||||
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
|
||||
[datetime.datetime(2005, 3, 20)]
|
||||
|
||||
``none()``
|
||||
~~~~~~~~~~
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Returns an ``EmptyQuerySet`` -- a ``QuerySet`` that always evaluates to
|
||||
an empty list. This can be used in cases where you know that you should
|
||||
return an empty result set and your caller is expecting a ``QuerySet``
|
||||
object (instead of returning an empty list, for example.)
|
||||
|
||||
Examples::
|
||||
|
||||
>>> Entry.objects.none()
|
||||
[]
|
||||
|
||||
``select_related()``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -186,7 +186,7 @@ code.
|
||||
|
||||
This is the philosophy behind `template inheritance`_.
|
||||
|
||||
.. _template inheritance: http://www.djangoproject.com/documentation/templates/#template-inheritance
|
||||
.. _template inheritance: ../templates/#template-inheritance
|
||||
|
||||
Be decoupled from HTML
|
||||
----------------------
|
||||
|
@ -53,7 +53,7 @@ Prints the admin-index template snippet for the given appnames.
|
||||
Use admin-index template snippets if you want to customize the look and feel of
|
||||
your admin's index page. See `Tutorial 2`_ for more information.
|
||||
|
||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
||||
.. _Tutorial 2: ../tutorial2/
|
||||
|
||||
createcachetable [tablename]
|
||||
----------------------------
|
||||
@ -61,7 +61,7 @@ createcachetable [tablename]
|
||||
Creates a cache table named ``tablename`` for use with the database cache
|
||||
backend. See the `cache documentation`_ for more information.
|
||||
|
||||
.. _cache documentation: http://www.djangoproject.com/documentation/cache/
|
||||
.. _cache documentation: ../cache/
|
||||
|
||||
dbshell
|
||||
-------
|
||||
@ -190,7 +190,7 @@ By default, the development server doesn't serve any static files for your site
|
||||
you want to configure Django to serve static media, read the `serving static files`_
|
||||
documentation.
|
||||
|
||||
.. _serving static files: http://www.djangoproject.com/documentation/static_files/
|
||||
.. _serving static files: ../static_files/
|
||||
|
||||
Turning off auto-reload
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -253,6 +253,8 @@ files are piped directly into the database after all of the models'
|
||||
table-creation statements have been executed. Use this SQL hook to populate
|
||||
tables with any necessary initial records, SQL functions or test data.
|
||||
|
||||
Note that the order in which the SQL files are processed is undefined.
|
||||
|
||||
sqlreset [appname appname ...]
|
||||
--------------------------------------
|
||||
|
||||
@ -380,7 +382,7 @@ Example usage::
|
||||
|
||||
django-admin.py syncdb --verbosity=2
|
||||
|
||||
Verbosity determines the amount of notification and debug information that
|
||||
Verbosity determines the amount of notification and debug information that
|
||||
will be printed to the console. '0' is no output, '1' is normal output,
|
||||
and `2` is verbose output.
|
||||
|
||||
|
@ -101,9 +101,9 @@ The "From:" header of the e-mail will be the value of the `SERVER_EMAIL setting`
|
||||
|
||||
This method exists for convenience and readability.
|
||||
|
||||
.. _ADMINS setting: http://www.djangoproject.com/documentation/settings/#admins
|
||||
.. _EMAIL_SUBJECT_PREFIX setting: http://www.djangoproject.com/documentation/settings/#email-subject-prefix
|
||||
.. _SERVER_EMAIL setting: http://www.djangoproject.com/documentation/settings/#server-email
|
||||
.. _ADMINS setting: ../settings/#admins
|
||||
.. _EMAIL_SUBJECT_PREFIX setting: ../settings/#email-subject-prefix
|
||||
.. _SERVER_EMAIL setting: ../settings/#server-email
|
||||
|
||||
mail_managers() function
|
||||
========================
|
||||
@ -114,7 +114,7 @@ Here's the definition::
|
||||
|
||||
mail_managers(subject, message, fail_silently=False)
|
||||
|
||||
.. _MANAGERS setting: http://www.djangoproject.com/documentation/settings/#managers
|
||||
.. _MANAGERS setting: ../settings/#managers
|
||||
|
||||
Examples
|
||||
========
|
||||
|
24
docs/faq.txt
24
docs/faq.txt
@ -63,7 +63,7 @@ at any level -- database servers, caching servers or Web/application servers.
|
||||
The framework cleanly separates components such as its database layer and
|
||||
application layer. And it ships with a simple-yet-powerful `cache framework`_.
|
||||
|
||||
.. _`cache framework`: http://www.djangoproject.com/documentation/cache/
|
||||
.. _`cache framework`: ../cache/
|
||||
|
||||
Who's behind this?
|
||||
------------------
|
||||
@ -191,7 +191,7 @@ Like we said: We're picky.
|
||||
|
||||
We've documented our philosophies on the `design philosophies page`_.
|
||||
|
||||
.. _design philosophies page: http://www.djangoproject.com/documentation/design_philosophies/
|
||||
.. _design philosophies page: ../design_philosophies/
|
||||
|
||||
Do you have any of those nifty "screencast" things?
|
||||
---------------------------------------------------
|
||||
@ -277,9 +277,9 @@ How do I get started?
|
||||
run into trouble.
|
||||
|
||||
.. _`Download the code`: http://www.djangoproject.com/download/
|
||||
.. _`installation guide`: http://www.djangoproject.com/documentation/install/
|
||||
.. _tutorial: http://www.djangoproject.com/documentation/tutorial1/
|
||||
.. _documentation: http://www.djangoproject.com/documentation/
|
||||
.. _`installation guide`: ../install/
|
||||
.. _tutorial: ../tutorial1/
|
||||
.. _documentation: ../
|
||||
.. _ask questions: http://www.djangoproject.com/community/
|
||||
|
||||
How do I fix the "install a later version of setuptools" error?
|
||||
@ -337,7 +337,7 @@ If you just want to play around and develop things on your local computer, use
|
||||
the development Web server that comes with Django. Things should Just Work.
|
||||
|
||||
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
||||
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/
|
||||
.. _How to use Django with FastCGI: ../fastcgi/
|
||||
.. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
||||
|
||||
How do I install mod_python on Windows?
|
||||
@ -464,7 +464,7 @@ Can I use Django with a pre-existing database?
|
||||
|
||||
Yes. See `Integrating with a legacy database`_.
|
||||
|
||||
.. _`Integrating with a legacy database`: http://www.djangoproject.com/documentation/legacy_databases/
|
||||
.. _`Integrating with a legacy database`: ../legacy_databases/
|
||||
|
||||
If I make changes to a model, how do I update the database?
|
||||
-----------------------------------------------------------
|
||||
@ -511,7 +511,7 @@ type, create an initial data file and put something like this in it::
|
||||
As explained in the `SQL initial data file`_ documentation, this SQL file can
|
||||
contain arbitrary SQL, so you can make any sorts of changes you need to make.
|
||||
|
||||
.. _SQL initial data file: http://www.djangoproject.com/documentation/model_api/#providing-initial-sql-data
|
||||
.. _SQL initial data file: ../model_api/#providing-initial-sql-data
|
||||
|
||||
Why is Django leaking memory?
|
||||
-----------------------------
|
||||
@ -592,7 +592,7 @@ My admin-site CSS and images showed up fine using the development server, but th
|
||||
See `serving the admin files`_ in the "How to use Django with mod_python"
|
||||
documentation.
|
||||
|
||||
.. _serving the admin files: http://www.djangoproject.com/documentation/modpython/#serving-the-admin-files
|
||||
.. _serving the admin files: ../modpython/#serving-the-admin-files
|
||||
|
||||
My "list_filter" contains a ManyToManyField, but the filter doesn't display.
|
||||
----------------------------------------------------------------------------
|
||||
@ -630,7 +630,7 @@ site is built using semantic HTML and plenty of CSS hooks, so any changes you'd
|
||||
like to make should be possible by editing the stylesheet. We've got a
|
||||
`guide to the CSS used in the admin`_ to get you started.
|
||||
|
||||
.. _`guide to the CSS used in the admin`: http://www.djangoproject.com/documentation/admin_css/
|
||||
.. _`guide to the CSS used in the admin`: ../admin_css/
|
||||
|
||||
How do I create users without having to edit password hashes?
|
||||
-------------------------------------------------------------
|
||||
@ -640,7 +640,7 @@ development version, where this problem was fixed on Aug. 4, 2006.
|
||||
|
||||
You can also use the Python API. See `creating users`_ for full info.
|
||||
|
||||
.. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users
|
||||
.. _creating users: ../authentication/#creating-users
|
||||
|
||||
Contributing code
|
||||
=================
|
||||
@ -651,7 +651,7 @@ How can I get started contributing code to Django?
|
||||
Thanks for asking! We've written an entire document devoted to this question.
|
||||
It's titled `Contributing to Django`_.
|
||||
|
||||
.. _Contributing to Django: http://www.djangoproject.com/documentation/contributing/
|
||||
.. _Contributing to Django: ../contributing/
|
||||
|
||||
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
@ -17,7 +17,7 @@ served with no startup time. Unlike mod_python (or `mod_perl`_), a FastCGI
|
||||
process doesn't run inside the Web server process, but in a separate,
|
||||
persistent process.
|
||||
|
||||
.. _current preferred setup: http://www.djangoproject.com/documentation/modpython/
|
||||
.. _current preferred setup: ../modpython/
|
||||
.. _Apache: http://httpd.apache.org/
|
||||
.. _mod_python: http://www.modpython.org/
|
||||
.. _mod_perl: http://perl.apache.org/
|
||||
|
@ -29,8 +29,8 @@ To install the flatpages app, follow these steps:
|
||||
to your MIDDLEWARE_CLASSES_ setting.
|
||||
3. Run the command ``manage.py syncdb``.
|
||||
|
||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
||||
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes
|
||||
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||
.. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
|
||||
|
||||
How it works
|
||||
============
|
||||
@ -63,9 +63,9 @@ resort.
|
||||
|
||||
For more on middleware, read the `middleware docs`_.
|
||||
|
||||
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id
|
||||
.. _RequestContext: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
|
||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _SITE_ID: ../settings/#site-id
|
||||
.. _RequestContext: ../templates_python/#subclassing-context-djangocontext
|
||||
.. _middleware docs: ../middleware/
|
||||
|
||||
How to add, change and delete flatpages
|
||||
=======================================
|
||||
@ -84,9 +84,9 @@ Flatpages are represented by a standard `Django model`_, which lives in
|
||||
`django/contrib/flatpages/models.py`_. You can access flatpage objects via the
|
||||
`Django database API`_.
|
||||
|
||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _Django model: ../model_api/
|
||||
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
|
||||
.. _Django database API: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _Django database API: ../db_api/
|
||||
|
||||
Flatpage templates
|
||||
==================
|
||||
|
@ -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
|
||||
that document to understand how we're making the switch.
|
||||
|
||||
.. _newforms documentation: http://www.djangoproject.com/documentation/newforms/
|
||||
.. _newforms documentation: ../newforms/
|
||||
|
||||
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_
|
||||
document for more details).
|
||||
|
||||
.. _`generic views`: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _`models API`: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _settings: http://www.djangoproject.com/documentation/settings/
|
||||
.. _`generic views`: ../generic_views/
|
||||
.. _`models API`: ../model_api/
|
||||
.. _settings: ../settings/
|
||||
|
@ -71,7 +71,7 @@ are first evaluated, so if you want to pass in a QuerySet via
|
||||
``extra_context`` that is always fresh you need to wrap it in a function or
|
||||
lambda that returns the QuerySet.
|
||||
|
||||
.. _database API docs: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _database API docs: ../db_api/
|
||||
|
||||
"Simple" generic views
|
||||
======================
|
||||
@ -99,7 +99,7 @@ which is a dictionary of the parameters captured in the URL.
|
||||
dictionary is callable, the generic view will call it
|
||||
just before rendering the template. (**This is new in the
|
||||
Django development version.**)
|
||||
|
||||
|
||||
**Example:**
|
||||
|
||||
Given the following URL patterns::
|
||||
@ -205,11 +205,11 @@ If ``template_name`` isn't specified, this view will use the template
|
||||
``<app_label>/<model_name>_archive.html`` by default, where:
|
||||
|
||||
* ``<model_name>`` is your model's name in all lowercase. For a model
|
||||
``StaffMember``, that'd be ``staffmember``.
|
||||
``StaffMember``, that'd be ``staffmember``.
|
||||
|
||||
* ``<app_label>`` is the right-most part of the full Python path to
|
||||
your model's app. For example, if your model lives in
|
||||
``apps/blog/models.py``, that'd be ``blog``.
|
||||
your model's app. For example, if your model lives in
|
||||
``apps/blog/models.py``, that'd be ``blog``.
|
||||
|
||||
**Template context:**
|
||||
|
||||
@ -223,7 +223,7 @@ In addition to ``extra_context``, the template's context will be:
|
||||
by ``date_field``. For example, if ``num_latest`` is ``10``, then
|
||||
``latest`` will be a list of the latest 10 objects in ``queryset``.
|
||||
|
||||
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
|
||||
.. _RequestContext docs: ../templates_python/#subclassing-context-djangocontext
|
||||
|
||||
``django.views.generic.date_based.archive_year``
|
||||
------------------------------------------------
|
||||
@ -266,9 +266,9 @@ to ``True``.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``make_object_list``: A boolean specifying whether to retrieve the full
|
||||
list of objects for this year and pass those to the template. If ``True``,
|
||||
@ -360,9 +360,9 @@ date in the *future* are not displayed unless you set ``allow_future`` to
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -441,9 +441,9 @@ in the *future* are not displayed unless you set ``allow_future`` to ``True``.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -526,9 +526,9 @@ you set ``allow_future`` to ``True``.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -638,7 +638,7 @@ future, the view will throw a 404 error by default, unless you set
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -710,9 +710,9 @@ A page representing a list of objects.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
to use in the template context. By default, this is ``'object'``. The
|
||||
view will append ``'_list'`` to the value of this parameter in
|
||||
determining the variable's name.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -824,7 +824,7 @@ A page representing an individual object.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
|
||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
||||
@ -916,8 +916,8 @@ In addition to ``extra_context``, the template's context will be:
|
||||
See the `manipulator and formfield documentation`_ for more information
|
||||
about using ``FormWrapper`` objects in templates.
|
||||
|
||||
.. _authentication system: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _manipulator and formfield documentation: http://www.djangoproject.com/documentation/forms/
|
||||
.. _authentication system: ../authentication/
|
||||
.. _manipulator and formfield documentation: ../forms/
|
||||
|
||||
``django.views.generic.create_update.update_object``
|
||||
----------------------------------------------------
|
||||
@ -973,7 +973,7 @@ object. This uses the automatic manipulators that come with Django models.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
|
||||
**Template name:**
|
||||
|
||||
@ -1054,7 +1054,7 @@ contain a form that POSTs to the same URL.
|
||||
the view's template. See the `RequestContext docs`_.
|
||||
|
||||
* ``template_object_name``: Designates the name of the template variable
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
to use in the template context. By default, this is ``'object'``.
|
||||
|
||||
**Template name:**
|
||||
|
||||
|
@ -53,7 +53,7 @@ internationalization machinery. See the `documentation for USE_I18N`_.
|
||||
You'll probably also want to remove ``'django.core.context_processors.i18n'``
|
||||
from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
|
||||
|
||||
.. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n
|
||||
.. _documentation for USE_I18N: ../settings/#use-i18n
|
||||
|
||||
How to specify translation strings
|
||||
==================================
|
||||
@ -175,7 +175,7 @@ class, though::
|
||||
verbose_name = _('my thing')
|
||||
verbose_name_plural = _('mythings')
|
||||
|
||||
.. _Django models: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _Django models: ../model_api/
|
||||
|
||||
Pluralization
|
||||
~~~~~~~~~~~~~
|
||||
@ -274,7 +274,7 @@ translation string. Example::
|
||||
In this case, both the tag and the filter will see the already-translated
|
||||
string, so they don't need to be aware of translations.
|
||||
|
||||
.. _Django templates: http://www.djangoproject.com/documentation/templates_python/
|
||||
.. _Django templates: ../templates_python/
|
||||
|
||||
How to create language files
|
||||
============================
|
||||
@ -394,7 +394,7 @@ That's it. Your translations are ready for use.
|
||||
please let us know! See `Submitting and maintaining translations`_ for
|
||||
the steps to take.
|
||||
|
||||
.. _Submitting and maintaining translations: http://www.djangoproject.com/documentation/contributing/
|
||||
.. _Submitting and maintaining translations: ../contributing/
|
||||
|
||||
How Django discovers language preference
|
||||
========================================
|
||||
@ -472,7 +472,7 @@ Notes:
|
||||
selection to German and English (and any sublanguage, like de-ch or
|
||||
en-us).
|
||||
|
||||
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
|
||||
.. _LANGUAGES setting: ../settings/#languages
|
||||
|
||||
* If you define a custom ``LANGUAGES`` setting, as explained in the
|
||||
previous bullet, it's OK to mark the languages as translation strings
|
||||
@ -530,10 +530,10 @@ Note that, with static (middleware-less) translation, the language is in
|
||||
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
|
||||
in ``request.LANGUAGE_CODE``.
|
||||
|
||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
||||
.. _middleware documentation: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _session: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
||||
.. _settings file: ../settings/
|
||||
.. _middleware documentation: ../middleware/
|
||||
.. _session: ../sessions/
|
||||
.. _request object: ../request_response/#httprequest-objects
|
||||
|
||||
The ``set_language`` redirect view
|
||||
==================================
|
||||
@ -599,7 +599,7 @@ message file. The choice is yours.
|
||||
of the settings file to determine this, and a settings file doesn't exist
|
||||
if you're manually configuring your settings.)
|
||||
|
||||
.. _settings documentation: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||
.. _settings documentation: ../settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||
|
||||
All message file repositories are structured the same way. They are:
|
||||
|
||||
|
@ -38,7 +38,7 @@ each platform.
|
||||
.. _Apache: http://httpd.apache.org/
|
||||
.. _mod_python: http://www.modpython.org/
|
||||
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
||||
.. _How to use Django with mod_python: http://www.djangoproject.com/documentation/modpython/
|
||||
.. _How to use Django with mod_python: ../modpython/
|
||||
.. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
||||
|
||||
Get your database running
|
||||
@ -113,14 +113,14 @@ latest bug fixes and improvements, follow these instructions:
|
||||
|
||||
svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django
|
||||
|
||||
4. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
|
||||
3. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
|
||||
system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
|
||||
(Windows). This step simply lets you type ``django-admin.py`` from within
|
||||
any directory, rather than having to qualify the command with the full path
|
||||
to the file.
|
||||
|
||||
You *don't* have to run ``python setup.py install``, because that command
|
||||
takes care of steps 3 and 4 for you.
|
||||
takes care of steps 2 and 3 for you.
|
||||
|
||||
When you want to update your copy of the Django source code, just run the
|
||||
command ``svn update`` from within the ``django`` directory. When you do this,
|
||||
|
@ -9,7 +9,7 @@ utilities to automate as much of this process as possible.
|
||||
This document assumes you know the Django basics, as covered in the
|
||||
`official tutorial`_.
|
||||
|
||||
.. _official tutorial: http://www.djangoproject.com/documentation/tutorial1/
|
||||
.. _official tutorial: ../tutorial1/
|
||||
|
||||
Give Django your database parameters
|
||||
====================================
|
||||
@ -25,13 +25,13 @@ what the name of the database is. Do that by editing these settings in your
|
||||
* `DATABASE_HOST`_
|
||||
* `DATABASE_PORT`_
|
||||
|
||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
||||
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name
|
||||
.. _DATABASE_ENGINE: http://www.djangoproject.com/documentation/settings/#database-engine
|
||||
.. _DATABASE_USER: http://www.djangoproject.com/documentation/settings/#database-user
|
||||
.. _DATABASE_PASSWORD: http://www.djangoproject.com/documentation/settings/#database-password
|
||||
.. _DATABASE_HOST: http://www.djangoproject.com/documentation/settings/#database-host
|
||||
.. _DATABASE_PORT: http://www.djangoproject.com/documentation/settings/#database-port
|
||||
.. _settings file: ../settings/
|
||||
.. _DATABASE_NAME: ../settings/#database-name
|
||||
.. _DATABASE_ENGINE: ../settings/#database-engine
|
||||
.. _DATABASE_USER: ../settings/#database-user
|
||||
.. _DATABASE_PASSWORD: ../settings/#database-password
|
||||
.. _DATABASE_HOST: ../settings/#database-host
|
||||
.. _DATABASE_PORT: ../settings/#database-port
|
||||
|
||||
Auto-generate the models
|
||||
========================
|
||||
@ -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
|
||||
``INSTALLED_APPS`` setting.
|
||||
|
||||
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _django-admin.py documentation: ../django_admin/
|
||||
|
||||
Install the core Django tables
|
||||
==============================
|
||||
|
@ -47,7 +47,7 @@ Enables site-wide cache. If this is enabled, each Django-powered page will be
|
||||
cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See
|
||||
the `cache documentation`_.
|
||||
|
||||
.. _`cache documentation`: http://www.djangoproject.com/documentation/cache/#the-per-site-cache
|
||||
.. _`cache documentation`: ../cache/#the-per-site-cache
|
||||
|
||||
django.middleware.common.CommonMiddleware
|
||||
-----------------------------------------
|
||||
@ -106,9 +106,10 @@ django.middleware.http.SetRemoteAddrFromForwardedFor
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Sets ``request['REMOTE_ADDR']`` based on ``request.['HTTP_X_FORWARDED_FOR']``,
|
||||
if the latter is set. This is useful if you're sitting behind a reverse proxy
|
||||
that causes each request's ``REMOTE_ADDR`` to be set to ``127.0.0.1``.
|
||||
Sets ``request.META['REMOTE_ADDR']`` based on
|
||||
``request.META['HTTP_X_FORWARDED_FOR']``, if the latter is set. This is useful
|
||||
if you're sitting behind a reverse proxy that causes each request's
|
||||
``REMOTE_ADDR`` to be set to ``127.0.0.1``.
|
||||
|
||||
**Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're
|
||||
not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do
|
||||
@ -122,7 +123,7 @@ django.contrib.sessions.middleware.SessionMiddleware
|
||||
|
||||
Enables session support. See the `session documentation`_.
|
||||
|
||||
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _`session documentation`: ../sessions/
|
||||
|
||||
django.contrib.auth.middleware.AuthenticationMiddleware
|
||||
-------------------------------------------------------
|
||||
@ -130,7 +131,7 @@ django.contrib.auth.middleware.AuthenticationMiddleware
|
||||
Adds the ``user`` attribute, representing the currently-logged-in user, to
|
||||
every incoming ``HttpRequest`` object. See `Authentication in Web requests`_.
|
||||
|
||||
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests
|
||||
.. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
|
||||
|
||||
django.middleware.transaction.TransactionMiddleware
|
||||
---------------------------------------------------
|
||||
@ -146,7 +147,7 @@ the same transaction control as the view functions.
|
||||
|
||||
See the `transaction management documentation`_.
|
||||
|
||||
.. _`transaction management documentation`: http://www.djangoproject.com/documentation/transactions/
|
||||
.. _`transaction management documentation`: ../transactions/
|
||||
|
||||
Writing your own middleware
|
||||
===========================
|
||||
|
@ -874,6 +874,10 @@ the relationship should work. All are optional:
|
||||
force Django to add the descriptor for the reverse
|
||||
relationship, allowing ``ManyToMany`` relationships to be
|
||||
non-symmetrical.
|
||||
|
||||
``db_table`` The name of the table to create for storing the many-to-many
|
||||
data. If this is not provided, Django will assume a default
|
||||
name based upon the names of the two tables being joined.
|
||||
|
||||
======================= ============================================================
|
||||
|
||||
@ -1268,6 +1272,24 @@ A few special cases to note about ``list_display``:
|
||||
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
|
||||
colored_name.allow_tags = True
|
||||
|
||||
* If the string given is a method of the model that returns True or False
|
||||
Django will display a pretty "on" or "off" icon if you give the method a
|
||||
``boolean`` attribute whose value is ``True``.
|
||||
|
||||
Here's a full example model::
|
||||
|
||||
class Person(models.Model):
|
||||
first_name = models.CharField(maxlength=50)
|
||||
birthday = models.DateField()
|
||||
|
||||
class Admin:
|
||||
list_display = ('name', 'born_in_fifties')
|
||||
|
||||
def born_in_fifties(self):
|
||||
return self.birthday.strftime('%Y')[:3] == 5
|
||||
born_in_fifties.boolean = True
|
||||
|
||||
|
||||
* The ``__str__()`` method is just as valid in ``list_display`` as any
|
||||
other model method, so it's perfectly OK to do this::
|
||||
|
||||
@ -1390,7 +1412,10 @@ This should be set to a list of field names that will be searched whenever
|
||||
somebody submits a search query in that text box.
|
||||
|
||||
These fields should be some kind of text field, such as ``CharField`` or
|
||||
``TextField``.
|
||||
``TextField``. You can also perform a related lookup on a ``ForeignKey`` with
|
||||
the lookup API "follow" notation::
|
||||
|
||||
search_fields = ['foreign_key__related_fieldname']
|
||||
|
||||
When somebody does a search in the admin search box, Django splits the search
|
||||
query into words and returns all objects that contain each of the words, case
|
||||
|
@ -20,7 +20,7 @@ You may also be interested in `How to use Django with FastCGI`_.
|
||||
.. _mod_perl: http://perl.apache.org/
|
||||
.. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html
|
||||
.. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html
|
||||
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/
|
||||
.. _How to use Django with FastCGI: ../fastcgi/
|
||||
|
||||
Basic configuration
|
||||
===================
|
||||
|
@ -2,9 +2,9 @@
|
||||
The newforms library
|
||||
====================
|
||||
|
||||
``django.newforms`` is a new replacement for ``django.forms``, the old Django
|
||||
form/manipulator/validation framework. This document explains how to use this
|
||||
new form library.
|
||||
``django.newforms`` is Django's fantastic new form-handling library. It's a
|
||||
replacement for ``django.forms``, the old form/manipulator/validation
|
||||
framework. This document explains how to use this new library.
|
||||
|
||||
Migration plan
|
||||
==============
|
||||
@ -51,7 +51,7 @@ too messy. The choice is yours.
|
||||
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
|
||||
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
|
||||
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
|
||||
============
|
||||
@ -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
|
||||
fields, in a declarative style that you'll be familiar with if you've used
|
||||
Django database models. In this section, we'll iteratively develop a form
|
||||
object that you might to implement "contact me" functionality on your personal
|
||||
Web site.
|
||||
object that you might use to implement "contact me" functionality on your
|
||||
personal Web site.
|
||||
|
||||
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`` --
|
||||
shortly.
|
||||
|
||||
Creating ``Form`` instances
|
||||
---------------------------
|
||||
|
||||
A ``Form`` instance is either **bound** or **unbound** to a set of data.
|
||||
|
||||
* If it's **bound** to a set of data, it's capable of validating that data
|
||||
and rendering the form as HTML with the data displayed in the HTML.
|
||||
|
||||
* If it's **unbound**, it cannot do validation (because there's no data to
|
||||
validate!), but it can still render the blank form as HTML.
|
||||
|
||||
To create an unbound ``Form`` instance, simply instantiate the class::
|
||||
|
||||
>>> f = ContactForm()
|
||||
|
||||
To bind data to a form, pass the data as a dictionary as the first parameter to
|
||||
your ``Form`` class constructor::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
|
||||
In this dictionary, the keys are the field names, which correspond to the
|
||||
attributes in your ``Form`` class. The values are the data you're trying
|
||||
to validate. These will usually be strings, but there's no requirement that
|
||||
they be strings; the type of data you pass depends on the ``Field``, as we'll
|
||||
see in a moment.
|
||||
|
||||
If you need to distinguish between bound and unbound form instances at runtime,
|
||||
check the value of the form's ``is_bound`` attribute::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.is_bound
|
||||
False
|
||||
>>> f = ContactForm({'subject': 'hello'})
|
||||
>>> f.is_bound
|
||||
True
|
||||
|
||||
Note that passing an empty dictionary creates a *bound* form with empty data::
|
||||
|
||||
>>> f = ContactForm({})
|
||||
>>> f.is_bound
|
||||
True
|
||||
|
||||
If you have a bound ``Form`` instance and want to change the data somehow, or
|
||||
if you want to bind an unbound ``Form`` instance to some data, create another
|
||||
``Form`` instance. There is no way to change data in a ``Form`` instance. Once
|
||||
a ``Form`` instance has been created, you should consider its data immutable,
|
||||
whether it has data or not.
|
||||
|
||||
Using forms to validate data
|
||||
----------------------------
|
||||
|
||||
The primary task of a ``Form`` object is to validate data. With a bound
|
||||
``Form`` instance, call the ``is_valid()`` method to run validation and return
|
||||
a boolean designating whether the data was valid::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
True
|
||||
|
||||
Let's try with some invalid data. In this case, ``subject`` is blank (an error,
|
||||
because all fields are required by default) and ``sender`` is not a valid
|
||||
e-mail address::
|
||||
|
||||
>>> data = {'subject': '',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'invalid e-mail address',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
False
|
||||
|
||||
Access the ``Form`` attribute ``errors`` to get a dictionary of error messages::
|
||||
|
||||
>>> f.errors
|
||||
{'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']}
|
||||
|
||||
In this dictionary, the keys are the field names, and the values are lists of
|
||||
Unicode strings representing the error messages. The error messages are stored
|
||||
in lists because a field can have multiple error messages.
|
||||
|
||||
You can access ``errors`` without having to call ``is_valid()`` first. The
|
||||
form's data will be validated the first time either you call ``is_valid()`` or
|
||||
access ``errors``.
|
||||
|
||||
Behavior of unbound forms
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It's meaningless to validate a form with no data, but, for the record, here's
|
||||
what happens with unbound forms::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.is_valid()
|
||||
False
|
||||
>>> f.errors
|
||||
{}
|
||||
|
||||
Accessing "clean" data
|
||||
----------------------
|
||||
|
||||
Each ``Field`` in a ``Form`` class is responsible not only for validating data,
|
||||
but also for "cleaning" it -- normalizing it to a consistent format. This is a
|
||||
nice feature, because it allows data for a particular field to be input in
|
||||
a variety of ways, always resulting in consistent output.
|
||||
|
||||
For example, ``DateField`` normalizes input into a Python ``datetime.date``
|
||||
object. Regardless of whether you pass it a string in the format
|
||||
``'1994-07-15'``, a ``datetime.date`` object or a number of other formats,
|
||||
``DateField`` will always normalize it to a ``datetime.date`` object as long as
|
||||
it's valid.
|
||||
|
||||
Once you've created a ``Form`` instance with a set of data and validated it,
|
||||
you can access the clean data via the ``clean_data`` attribute of the ``Form``
|
||||
object::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.clean_data
|
||||
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||
|
||||
Note that any text-based field -- such as ``CharField`` or ``EmailField`` --
|
||||
always cleans the input into a Unicode string. We'll cover the encoding
|
||||
implications later in this document.
|
||||
|
||||
If your data does *not* validate, your ``Form`` instance will not have a
|
||||
``clean_data`` attribute::
|
||||
|
||||
>>> data = {'subject': '',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'invalid e-mail address',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
False
|
||||
>>> f.clean_data
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
||||
|
||||
``clean_data`` will always *only* contain a key for fields defined in the
|
||||
``Form``, even if you pass extra data when you define the ``Form``. In this
|
||||
example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
|
||||
but ``clean_data`` contains only the form's fields::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True,
|
||||
... 'extra_field_1': 'foo',
|
||||
... 'extra_field_2': 'bar',
|
||||
... 'extra_field_3': 'baz'}
|
||||
>>> f = ContactForm(data)
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.clean_data # Doesn't contain extra_field_1, etc.
|
||||
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||
|
||||
Behavior of unbound forms
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It's meaningless to request "clean" data in a form with no data, but, for the
|
||||
record, here's what happens with unbound forms::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.clean_data
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
||||
|
||||
Outputting forms as HTML
|
||||
------------------------
|
||||
|
||||
The first thing we can do with this is output it as HTML. To do so, instantiate
|
||||
it and ``print`` it::
|
||||
The second task of a ``Form`` object is to render itself as HTML. To do so,
|
||||
simply ``print`` it::
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> 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_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
|
||||
|
||||
If the form is bound to data, the HTML output will include that data
|
||||
appropriately. For example, if a field is represented by an
|
||||
``<input type="text">``, the data will be in the ``value`` attribute. If a
|
||||
field is represented by an ``<input type="checkbox">``, then that HTML will
|
||||
include ``checked="checked"`` if appropriate::
|
||||
|
||||
>>> data = {'subject': 'hello',
|
||||
... 'message': 'Hi there',
|
||||
... 'sender': 'foo@example.com',
|
||||
... 'cc_myself': True}
|
||||
>>> f = ContactForm(data)
|
||||
>>> print f
|
||||
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr>
|
||||
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr>
|
||||
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></td></tr>
|
||||
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
|
||||
|
||||
This default output is a two-column HTML table, with a ``<tr>`` for each field.
|
||||
Notice the following:
|
||||
|
||||
@ -125,13 +325,13 @@ Notice the following:
|
||||
``EmailField`` are represented by an ``<input type="text">``.
|
||||
``BooleanField`` is represented by an ``<input type="checkbox">``. Note
|
||||
these are merely sensible defaults; you can specify which HTML to use for
|
||||
a given field by using ``widgets``, which we'll explain shortly.
|
||||
a given field by using widgets, which we'll explain shortly.
|
||||
|
||||
* The HTML ``name`` for each tag is taken directly from its attribute name
|
||||
in the ``ContactForm`` class.
|
||||
|
||||
* The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and
|
||||
``'CC myself:'`` is generated from the field name by converting all
|
||||
``'Cc myself:'`` is generated from the field name by converting all
|
||||
underscores to spaces and upper-casing the first letter. Again, note
|
||||
these are merely sensible defaults; you can also specify labels manually.
|
||||
|
||||
@ -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
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -322,6 +553,271 @@ The field-specific output honors the form object's ``auto_id`` setting::
|
||||
>>> print f['message']
|
||||
<input type="text" name="message" id="id_message" />
|
||||
|
||||
For a field's list of errors, access the field's ``errors`` attribute. This
|
||||
is a list-like object that is displayed as an HTML ``<ul>`` when printed::
|
||||
|
||||
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
|
||||
>>> f = ContactForm(data, auto_id=False)
|
||||
>>> print f['message']
|
||||
<input type="text" name="message" />
|
||||
>>> f['message'].errors
|
||||
[u'This field is required.']
|
||||
>>> print f['message'].errors
|
||||
<ul class="errorlist"><li>This field is required.</li></ul>
|
||||
>>> f['subject'].errors
|
||||
[]
|
||||
>>> print f['subject'].errors
|
||||
|
||||
>>> str(f['subject'].errors)
|
||||
''
|
||||
|
||||
Fields
|
||||
======
|
||||
|
||||
When you create a ``Form`` class, the most important part is defining the
|
||||
fields of the form. Each field has custom validation logic, along with a few
|
||||
other hooks.
|
||||
|
||||
Although the primary way you'll use ``Field`` classes is in ``Form`` classes,
|
||||
you can also instantiate them and use them directly to get a better idea of
|
||||
how they work. Each ``Field`` instance has a ``clean()`` method, which takes
|
||||
a single argument and either raises a ``django.newforms.ValidationError``
|
||||
exception or returns the clean value::
|
||||
|
||||
>>> f = forms.EmailField()
|
||||
>>> f.clean('foo@example.com')
|
||||
u'foo@example.com'
|
||||
>>> f.clean(u'foo@example.com')
|
||||
u'foo@example.com'
|
||||
>>> f.clean('invalid e-mail address')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid e-mail address.']
|
||||
|
||||
If you've used Django's old forms/validation framework, take care in noticing
|
||||
this ``ValidationError`` is different than the previous ``ValidationError``.
|
||||
This one lives at ``django.newforms.ValidationError`` rather than
|
||||
``django.core.validators.ValidationError``.
|
||||
|
||||
Core field arguments
|
||||
--------------------
|
||||
|
||||
Each ``Field`` class constructor takes at least these arguments. Some
|
||||
``Field`` classes take additional, field-specific arguments, but the following
|
||||
should *always* be available:
|
||||
|
||||
``required``
|
||||
~~~~~~~~~~~~
|
||||
|
||||
By default, each ``Field`` class assumes the value is required, so if you pass
|
||||
an empty value -- either ``None`` or the empty string (``""``) -- then
|
||||
``clean()`` will raise a ``ValidationError`` exception::
|
||||
|
||||
>>> f = forms.CharField()
|
||||
>>> f.clean('foo')
|
||||
u'foo'
|
||||
>>> f.clean('')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'This field is required.']
|
||||
>>> f.clean(None)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'This field is required.']
|
||||
>>> f.clean(' ')
|
||||
u' '
|
||||
>>> f.clean(0)
|
||||
u'0'
|
||||
>>> f.clean(True)
|
||||
u'True'
|
||||
>>> f.clean(False)
|
||||
u'False'
|
||||
|
||||
To specify that a field is *not* required, pass ``required=False`` to the
|
||||
``Field`` constructor::
|
||||
|
||||
>>> f = forms.CharField(required=False)
|
||||
>>> f.clean('foo')
|
||||
u'foo'
|
||||
>>> f.clean('')
|
||||
u''
|
||||
>>> f.clean(None)
|
||||
u''
|
||||
>>> f.clean(0)
|
||||
u'0'
|
||||
>>> f.clean(True)
|
||||
u'True'
|
||||
>>> f.clean(False)
|
||||
u'False'
|
||||
|
||||
If a ``Field`` has ``required=False`` and you pass ``clean()`` an empty value,
|
||||
then ``clean()`` will return a *normalized* empty value rather than raising
|
||||
``ValidationError``. For ``CharField``, this will be a Unicode empty string.
|
||||
For other ``Field`` classes, it might be ``None``. (This varies from field to
|
||||
field.)
|
||||
|
||||
``label``
|
||||
~~~~~~~~~
|
||||
|
||||
The ``label`` argument lets you specify the "human-friendly" label for this
|
||||
field. This is used when the ``Field`` is displayed in a ``Form``.
|
||||
|
||||
As explained in _`Outputting forms as HTML` above, the default label for a
|
||||
``Field`` is generated from the field name by converting all underscores to
|
||||
spaces and upper-casing the first letter. Specify ``label`` if that default
|
||||
behavior doesn't result in an adequate label.
|
||||
|
||||
Here's a full example ``Form`` that implements ``label`` for two of its fields.
|
||||
We've specified ``auto_id=False`` to simplify the output::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField(label='Your name')
|
||||
... url = forms.URLField(label='Your Web site', required=False)
|
||||
... comment = forms.CharField()
|
||||
>>> f = CommentForm(auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Your name:</th><td><input type="text" name="name" /></td></tr>
|
||||
<tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
|
||||
``initial``
|
||||
~~~~~~~~~~~
|
||||
|
||||
The ``initial`` argument lets you specify the initial value to use when
|
||||
rendering this ``Field`` in an unbound ``Form``.
|
||||
|
||||
The use-case for this is when you want to display an "empty" form in which a
|
||||
field is initialized to a particular value. For example::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField(initial='Your name')
|
||||
... url = forms.URLField(initial='http://')
|
||||
... comment = forms.CharField()
|
||||
>>> f = CommentForm(auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
|
||||
<tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
|
||||
You may be thinking, why not just pass a dictionary of the initial values as
|
||||
data when displaying the form? Well, if you do that, you'll trigger validation,
|
||||
and the HTML output will include any validation errors::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField()
|
||||
... url = forms.URLField()
|
||||
... comment = forms.CharField()
|
||||
>>> default_data = {'name': 'Your name', 'url': 'http://'}
|
||||
>>> f = CommentForm(default_data, auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
|
||||
<tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr>
|
||||
<tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr>
|
||||
|
||||
This is why ``initial`` values are only displayed for unbound forms. For bound
|
||||
forms, the HTML output will use the bound data.
|
||||
|
||||
Also note that ``initial`` values are *not* used as "fallback" data in
|
||||
validation if a particular field's value is not given. ``initial`` values are
|
||||
*only* intended for initial form display::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField(initial='Your name')
|
||||
... url = forms.URLField(initial='http://')
|
||||
... comment = forms.CharField()
|
||||
>>> data = {'name': '', 'url': '', 'comment': 'Foo'}
|
||||
>>> f = CommentForm(data)
|
||||
>>> f.is_valid()
|
||||
False
|
||||
# The form does *not* fall back to using the initial values.
|
||||
>>> f.errors
|
||||
{'url': [u'This field is required.'], 'name': [u'This field is required.']}
|
||||
|
||||
``widget``
|
||||
~~~~~~~~~~
|
||||
|
||||
The ``widget`` argument lets you specify a ``Widget`` class to use when
|
||||
rendering this ``Field``. See _`Widgets` below for more information.
|
||||
|
||||
``help_text``
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The ``help_text`` argument lets you specify descriptive text for this
|
||||
``Field``. If you provide ``help_text``, it will be displayed next to the
|
||||
``Field`` when the ``Field`` is rendered in a ``Form``.
|
||||
|
||||
Here's a full example ``Form`` that implements ``help_text`` for two of its
|
||||
fields. We've specified ``auto_id=False`` to simplify the output::
|
||||
|
||||
>>> class HelpTextContactForm(forms.Form):
|
||||
... subject = forms.CharField(max_length=100, help_text='100 characters max.')
|
||||
... message = forms.CharField()
|
||||
... sender = forms.EmailField(help_text='A valid e-mail address, please.')
|
||||
... cc_myself = forms.BooleanField()
|
||||
>>> f = HelpTextContactForm(auto_id=False)
|
||||
>>> print f.as_table()
|
||||
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr>
|
||||
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
|
||||
<tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid e-mail address, please.</td></tr>
|
||||
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
|
||||
>>> print f.as_ul()
|
||||
<li>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</li>
|
||||
<li>Message: <input type="text" name="message" /></li>
|
||||
<li>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</li>
|
||||
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
|
||||
>>> print f.as_p()
|
||||
<p>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</p>
|
||||
<p>Message: <input type="text" name="message" /></p>
|
||||
<p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p>
|
||||
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
|
||||
|
||||
Dynamic initial values
|
||||
----------------------
|
||||
|
||||
The ``initial`` argument to ``Field`` (explained above) lets you hard-code the
|
||||
initial value for a ``Field`` -- but what if you want to declare the initial
|
||||
value at runtime? For example, you might want to fill in a ``username`` field
|
||||
with the username of the current session.
|
||||
|
||||
To accomplish this, use the ``initial`` argument to a ``Form``. This argument,
|
||||
if given, should be a dictionary mapping field names to initial values. Only
|
||||
include the fields for which you're specifying an initial value; it's not
|
||||
necessary to include every field in your form. For example::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField()
|
||||
... url = forms.URLField()
|
||||
... comment = forms.CharField()
|
||||
>>> f = CommentForm(initial={'name': 'your username'}, auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="your username" /></td></tr>
|
||||
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
>>> f = CommentForm(initial={'name': 'another username'}, auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="another username" /></td></tr>
|
||||
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
|
||||
Just like the ``initial`` parameter to ``Field``, these values are only
|
||||
displayed for unbound forms, and they're not used as fallback values if a
|
||||
particular value isn't provided.
|
||||
|
||||
Finally, note that if a ``Field`` defines ``initial`` *and* you include
|
||||
``initial`` when instantiating the ``Form``, then the latter ``initial`` will
|
||||
have precedence. In this example, ``initial`` is provided both at the field
|
||||
level and at the form instance level, and the latter gets precedence::
|
||||
|
||||
>>> class CommentForm(forms.Form):
|
||||
... name = forms.CharField(initial='class')
|
||||
... url = forms.URLField()
|
||||
... comment = forms.CharField()
|
||||
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="instance" /></td></tr>
|
||||
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||
|
||||
More coming soon
|
||||
================
|
||||
|
||||
@ -333,8 +829,5 @@ what's possible.
|
||||
If you're really itching to learn and use this library, please be patient.
|
||||
We're working hard on finishing both the code and documentation.
|
||||
|
||||
Using forms with templates
|
||||
==========================
|
||||
|
||||
Using forms in views
|
||||
====================
|
||||
Widgets
|
||||
=======
|
||||
|
@ -24,7 +24,7 @@ and Django's ``HttpResponse`` objects are file-like objects.
|
||||
|
||||
For more information on the CSV library, see the `CSV library docs`_.
|
||||
|
||||
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/
|
||||
.. _Request and response objects: ../request_response/
|
||||
.. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html
|
||||
|
||||
Here's an example::
|
||||
@ -115,5 +115,5 @@ a line of CSV for each row. It uses the `addslashes template filter`_ to ensure
|
||||
there aren't any problems with quotes. If you can be certain your data doesn't
|
||||
have single or double quotes in it, you can remove the ``addslashes`` filters.
|
||||
|
||||
.. _Django template system: http://www.djangoproject.com/documentation/templates/
|
||||
.. _addslashes template filter: http://www.djangoproject.com/documentation/templates/#addslashes
|
||||
.. _Django template system: ../templates/
|
||||
.. _addslashes template filter: ../templates/#addslashes
|
||||
|
@ -43,7 +43,7 @@ objects.
|
||||
For more information on ``HttpResponse`` objects, see
|
||||
`Request and response objects`_.
|
||||
|
||||
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/
|
||||
.. _Request and response objects: ../request_response/
|
||||
|
||||
Here's a "Hello World" example::
|
||||
|
||||
|
@ -11,7 +11,7 @@ understand how Django works, but this isn't intended to be a tutorial or
|
||||
reference. Please see our more-detailed Django documentation_ when you're ready
|
||||
to start a project.
|
||||
|
||||
.. _documentation: http://www.djangoproject.com/documentation/
|
||||
.. _documentation: ../
|
||||
|
||||
Design your model
|
||||
=================
|
||||
|
@ -15,8 +15,8 @@ To install the redirects app, follow these steps:
|
||||
to your MIDDLEWARE_CLASSES_ setting.
|
||||
3. Run the command ``manage.py syncdb``.
|
||||
|
||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
||||
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes
|
||||
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||
.. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
|
||||
|
||||
How it works
|
||||
============
|
||||
@ -46,8 +46,8 @@ resort.
|
||||
|
||||
For more on middleware, read the `middleware docs`_.
|
||||
|
||||
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id
|
||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _SITE_ID: ../settings/#site-id
|
||||
.. _middleware docs: ../middleware/
|
||||
|
||||
How to add, change and delete redirects
|
||||
=======================================
|
||||
@ -66,6 +66,6 @@ Redirects are represented by a standard `Django model`_, which lives in
|
||||
`django/contrib/redirects/models.py`_. You can access redirect
|
||||
objects via the `Django database API`_.
|
||||
|
||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
||||
.. _Django model: ../model_api/
|
||||
.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
|
||||
.. _Django database API: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _Django database API: ../db_api/
|
||||
|
@ -117,14 +117,14 @@ All attributes except ``session`` should be considered read-only.
|
||||
``AuthenticationMiddleware`` activated. For more, see
|
||||
`Authentication in Web requests`_.
|
||||
|
||||
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests
|
||||
.. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
|
||||
|
||||
``session``
|
||||
A readable-and-writable, dictionary-like object that represents the current
|
||||
session. This is only available if your Django installation has session
|
||||
support activated. See the `session documentation`_ for full details.
|
||||
|
||||
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _`session documentation`: ../sessions/
|
||||
|
||||
``raw_post_data``
|
||||
The raw HTTP POST data. This is only useful for advanced processing. Use
|
||||
|
@ -27,7 +27,7 @@ If you don't want to use sessions, you might as well remove the
|
||||
``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
|
||||
from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
|
||||
|
||||
.. _middleware: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _middleware: ../middleware/
|
||||
|
||||
Using sessions in views
|
||||
=======================
|
||||
@ -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 --
|
||||
that is, if any of its dictionary values have been assigned or deleted.
|
||||
|
||||
.. _Django settings: http://www.djangoproject.com/documentation/settings/
|
||||
.. _Django settings: ../settings/
|
||||
|
||||
Technical details
|
||||
=================
|
||||
|
@ -59,7 +59,7 @@ Use the ``--settings`` command-line argument to specify the settings manually::
|
||||
|
||||
django-admin.py runserver --settings=mysite.settings
|
||||
|
||||
.. _django-admin.py: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _django-admin.py: ../django_admin/
|
||||
|
||||
On the server (mod_python)
|
||||
--------------------------
|
||||
@ -75,7 +75,7 @@ settings file to use. Do that with ``SetEnv``::
|
||||
|
||||
Read the `Django mod_python documentation`_ for more information.
|
||||
|
||||
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/
|
||||
.. _Django mod_python documentation: ../modpython/
|
||||
|
||||
Default settings
|
||||
================
|
||||
@ -102,7 +102,7 @@ between the current settings file and Django's default settings.
|
||||
|
||||
For more, see the `diffsettings documentation`_.
|
||||
|
||||
.. _diffsettings documentation: http://www.djangoproject.com/documentation/django_admin/#diffsettings
|
||||
.. _diffsettings documentation: ../django_admin/#diffsettings
|
||||
|
||||
Using settings in Python code
|
||||
=============================
|
||||
@ -157,13 +157,13 @@ ABSOLUTE_URL_OVERRIDES
|
||||
|
||||
Default: ``{}`` (Empty dictionary)
|
||||
|
||||
A dictionary mapping ``"app_label.module_name"`` strings to functions that take
|
||||
A dictionary mapping ``"app_label.model_name"`` strings to functions that take
|
||||
a model object and return its URL. This is a way of overriding
|
||||
``get_absolute_url()`` methods on a per-installation basis. Example::
|
||||
|
||||
ABSOLUTE_URL_OVERRIDES = {
|
||||
'blogs.blogs': lambda o: "/blogs/%s/" % o.slug,
|
||||
'news.stories': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
|
||||
'blogs.Weblog': lambda o: "/blogs/%s/" % o.slug,
|
||||
'news.Story': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
|
||||
}
|
||||
|
||||
ADMIN_FOR
|
||||
@ -306,7 +306,7 @@ pages -- and, possibly, by other parts of the system. See
|
||||
|
||||
See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
||||
|
||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
||||
.. _allowed date format strings: ../templates/#now
|
||||
|
||||
DATETIME_FORMAT
|
||||
---------------
|
||||
@ -319,7 +319,7 @@ pages -- and, possibly, by other parts of the system. See
|
||||
|
||||
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
||||
|
||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
||||
.. _allowed date format strings: ../templates/#now
|
||||
|
||||
DEBUG
|
||||
-----
|
||||
@ -433,7 +433,7 @@ A tuple of strings designating all applications that are enabled in this Django
|
||||
installation. Each string should be a full Python path to a Python package that
|
||||
contains a Django application, as created by `django-admin.py startapp`_.
|
||||
|
||||
.. _django-admin.py startapp: http://www.djangoproject.com/documentation/django_admin/#startapp-appname
|
||||
.. _django-admin.py startapp: ../django_admin/#startapp-appname
|
||||
|
||||
INTERNAL_IPS
|
||||
------------
|
||||
@ -464,7 +464,7 @@ A string representing the language code for this installation. This should be
|
||||
in standard language format. For example, U.S. English is ``"en-us"``. See the
|
||||
`internationalization docs`_.
|
||||
|
||||
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _internationalization docs: ../i18n/
|
||||
|
||||
LANGUAGES
|
||||
---------
|
||||
@ -557,6 +557,11 @@ Default: ``''`` (Empty string)
|
||||
URL that handles the media served from ``MEDIA_ROOT``.
|
||||
Example: ``"http://media.lawrence.com"``
|
||||
|
||||
Note that this should have a trailing slash if it has a path component.
|
||||
|
||||
Good: ``"http://www.example.com/static/"``
|
||||
Bad: ``"http://www.example.com/static"``
|
||||
|
||||
MIDDLEWARE_CLASSES
|
||||
------------------
|
||||
|
||||
@ -612,7 +617,7 @@ Default: Not defined
|
||||
A string representing the full Python import path to your root URLconf. For example:
|
||||
``"mydjangoapps.urls"``. See `How Django processes a request`_.
|
||||
|
||||
.. _How Django processes a request: http://www.djangoproject.com/documentation/url_dispatch/#how-django-processes-a-request
|
||||
.. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request
|
||||
|
||||
SECRET_KEY
|
||||
----------
|
||||
@ -704,7 +709,7 @@ and a single database can manage content for multiple sites.
|
||||
|
||||
See the `site framework docs`_.
|
||||
|
||||
.. _site framework docs: http://www.djangoproject.com/documentation/sites/
|
||||
.. _site framework docs: ../sites/
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS
|
||||
---------------------------
|
||||
@ -760,7 +765,7 @@ Default: ``''`` (Empty string)
|
||||
Output, as a string, that the template system should use for invalid (e.g.
|
||||
misspelled) variables. See `How invalid variables are handled`_.
|
||||
|
||||
.. _How invalid variables are handled: http://www.djangoproject.com/documentation/templates_python/#how-invalid-variables-are-handled
|
||||
.. _How invalid variables are handled: ../templates_python/#how-invalid-variables-are-handled
|
||||
|
||||
TEST_RUNNER
|
||||
-----------
|
||||
@ -798,7 +803,7 @@ pages -- and, possibly, by other parts of the system. See
|
||||
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and
|
||||
MONTH_DAY_FORMAT.
|
||||
|
||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
||||
.. _allowed date format strings: ../templates/#now
|
||||
|
||||
TIME_ZONE
|
||||
---------
|
||||
@ -868,11 +873,11 @@ Different locales have different formats. For example, U.S. English would say
|
||||
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
|
||||
TIME_FORMAT and MONTH_DAY_FORMAT.
|
||||
|
||||
.. _cache docs: http://www.djangoproject.com/documentation/cache/
|
||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
||||
.. _session docs: http://www.djangoproject.com/documentation/sessions/
|
||||
.. _cache docs: ../cache/
|
||||
.. _middleware docs: ../middleware/
|
||||
.. _session docs: ../sessions/
|
||||
.. _See available choices: http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||
.. _template documentation: http://www.djangoproject.com/documentation/templates_python/
|
||||
.. _template documentation: ../templates_python/
|
||||
|
||||
Creating your own settings
|
||||
==========================
|
||||
|
@ -23,8 +23,8 @@ you express this information in Python code.
|
||||
It works much like Django's `syndication framework`_. To create a sitemap, just
|
||||
write a ``Sitemap`` class and point to it in your URLconf_.
|
||||
|
||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _syndication framework: ../syndication/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Installation
|
||||
============
|
||||
@ -41,9 +41,9 @@ To install the sitemap app, follow these steps:
|
||||
reason it needs to go into ``INSTALLED_APPS`` is so that the
|
||||
``load_template_source`` template loader can find the default templates.)
|
||||
|
||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
||||
.. _TEMPLATE_LOADERS: http://www.djangoproject.com/documentation/settings/#template-loaders
|
||||
.. _sites framework: http://www.djangoproject.com/documentation/sites/
|
||||
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||
.. _TEMPLATE_LOADERS: ../settings/#template-loaders
|
||||
.. _sites framework: ../sites/
|
||||
|
||||
Initialization
|
||||
==============
|
||||
@ -68,7 +68,7 @@ The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
|
||||
``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
|
||||
(e.g., ``BlogSitemap(some_var)``).
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Sitemap classes
|
||||
===============
|
||||
@ -217,8 +217,8 @@ defined for the current ``SITE_ID`` (see the `sites documentation`_) and
|
||||
creates an entry in the sitemap. These entries include only the ``location``
|
||||
attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
|
||||
|
||||
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
|
||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
||||
.. _flatpages: ../flatpages/
|
||||
.. _sites documentation: ../sites/
|
||||
|
||||
``GenericSitemap``
|
||||
------------------
|
||||
@ -232,7 +232,7 @@ the ``lastmod`` attribute in the generated sitemap. You may also pass
|
||||
``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
|
||||
constructor to specify these attributes for all URLs.
|
||||
|
||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _generic views: ../generic_views/
|
||||
|
||||
Example
|
||||
-------
|
||||
@ -261,7 +261,7 @@ Here's an example of a URLconf_ using both::
|
||||
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
||||
)
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Creating a sitemap index
|
||||
========================
|
||||
|
@ -276,8 +276,8 @@ you want your admin site to have access to all objects (not just site-specific
|
||||
ones), put ``objects = models.Manager()`` in your model, before you define
|
||||
``CurrentSiteManager``.
|
||||
|
||||
.. _manager: http://www.djangoproject.com/documentation/model_api/#managers
|
||||
.. _manager documentation: http://www.djangoproject.com/documentation/model_api/#managers
|
||||
.. _manager: ../model_api/#managers
|
||||
.. _manager documentation: ../model_api/#managers
|
||||
|
||||
How Django uses the sites framework
|
||||
===================================
|
||||
@ -316,7 +316,7 @@ Here's how Django uses the sites framework:
|
||||
* The shortcut view (``django.views.defaults.shortcut``) uses the domain of
|
||||
the current ``Site`` object when calculating an object's URL.
|
||||
|
||||
.. _redirects framework: http://www.djangoproject.com/documentation/redirects/
|
||||
.. _flatpages framework: http://www.djangoproject.com/documentation/flatpages/
|
||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _authentication framework: http://www.djangoproject.com/documentation/authentication/
|
||||
.. _redirects framework: ../redirects/
|
||||
.. _flatpages framework: ../flatpages/
|
||||
.. _syndication framework: ../syndication/
|
||||
.. _authentication framework: ../authentication/
|
||||
|
@ -24,7 +24,7 @@ production setting. Use this only for development.
|
||||
For information on serving static files in an Apache production environment,
|
||||
see the `Django mod_python documentation`_.
|
||||
|
||||
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/#serving-media-files
|
||||
.. _Django mod_python documentation: ../modpython/#serving-media-files
|
||||
|
||||
How to do it
|
||||
============
|
||||
@ -49,7 +49,7 @@ Examples:
|
||||
* The file ``/path/bar.jpg`` will not be accessible, because it doesn't
|
||||
fall under the document root.
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Directory listings
|
||||
==================
|
||||
@ -122,4 +122,4 @@ associated with the ``django.views.static.serve`` view. If not
|
||||
Of course, the catch here is that you'll have to remember to set ``DEBUG=False``
|
||||
in your production settings file. But you should be doing that anyway.
|
||||
|
||||
.. _DEBUG setting: http://www.djangoproject.com/documentation/settings/#debug
|
||||
.. _DEBUG setting: ../settings/#debug
|
||||
|
@ -26,7 +26,7 @@ to determine which feed to output.
|
||||
|
||||
To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
|
||||
Initialization
|
||||
--------------
|
||||
@ -72,8 +72,8 @@ The above example registers two feeds:
|
||||
|
||||
Once that's set up, you just need to define the ``Feed`` classes themselves.
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
||||
.. _URLconf: ../url_dispatch/
|
||||
.. _settings file: ../settings/
|
||||
|
||||
Feed classes
|
||||
------------
|
||||
@ -156,8 +156,8 @@ put into those elements.
|
||||
{{ obj.description }}
|
||||
|
||||
.. _chicagocrime.org: http://www.chicagocrime.org/
|
||||
.. _object-relational mapper: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _Django templates: http://www.djangoproject.com/documentation/templates/
|
||||
.. _object-relational mapper: ../db_api/
|
||||
.. _Django templates: ../templates/
|
||||
|
||||
A complex example
|
||||
-----------------
|
||||
@ -277,7 +277,7 @@ Feeds created by the syndication framework automatically include the
|
||||
appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
|
||||
comes directly from your `LANGUAGE_CODE setting`_.
|
||||
|
||||
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code
|
||||
.. _LANGUAGE_CODE setting: ../settings/#language-code
|
||||
|
||||
URLs
|
||||
----
|
||||
@ -292,7 +292,7 @@ Atom feeds require a ``<link rel="self">`` that defines the feed's current
|
||||
location. The syndication framework populates this automatically, using the
|
||||
domain of the current site according to the SITE_ID setting.
|
||||
|
||||
.. _SITE_ID setting: http://www.djangoproject.com/documentation/settings/#site-id
|
||||
.. _SITE_ID setting: ../settings/#site-id
|
||||
|
||||
Publishing Atom and RSS feeds in tandem
|
||||
---------------------------------------
|
||||
|
@ -792,7 +792,7 @@ Note that if you use ``{% ssi %}``, you'll need to define
|
||||
|
||||
See also: ``{% include %}``.
|
||||
|
||||
.. _ALLOWED_INCLUDE_ROOTS: http://www.djangoproject.com/documentation/settings/#allowed-include-roots
|
||||
.. _ALLOWED_INCLUDE_ROOTS: ../settings/#allowed-include-roots
|
||||
|
||||
templatetag
|
||||
~~~~~~~~~~~
|
||||
@ -1218,7 +1218,7 @@ django.contrib.humanize
|
||||
A set of Django template filters useful for adding a "human touch" to data. See
|
||||
the `humanize documentation`_.
|
||||
|
||||
.. _humanize documentation: http://www.djangoproject.com/documentation/add_ons/#humanize
|
||||
.. _humanize documentation: ../add_ons/#humanize
|
||||
|
||||
django.contrib.markup
|
||||
---------------------
|
||||
|
@ -11,7 +11,7 @@ If you're looking to use the Django template system as part of another
|
||||
application -- i.e., without the rest of the framework -- make sure to read
|
||||
the `configuration`_ section later in this document.
|
||||
|
||||
.. _`The Django template language: For template authors`: http://www.djangoproject.com/documentation/templates/
|
||||
.. _`The Django template language: For template authors`: ../templates/
|
||||
|
||||
Basics
|
||||
======
|
||||
@ -327,8 +327,8 @@ Note::
|
||||
|
||||
Here's what each of the default processors does:
|
||||
|
||||
.. _HttpRequest object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
||||
.. _TEMPLATE_CONTEXT_PROCESSORS setting: http://www.djangoproject.com/documentation/settings/#template-context-processors
|
||||
.. _HttpRequest object: ../request_response/#httprequest-objects
|
||||
.. _TEMPLATE_CONTEXT_PROCESSORS setting: ../settings/#template-context-processors
|
||||
|
||||
django.core.context_processors.auth
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -353,9 +353,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
||||
permissions that the currently logged-in user has. See the `permissions
|
||||
docs`_.
|
||||
|
||||
.. _user authentication docs: http://www.djangoproject.com/documentation/authentication/#users
|
||||
.. _message docs: http://www.djangoproject.com/documentation/authentication/#messages
|
||||
.. _permissions docs: http://www.djangoproject.com/documentation/authentication/#permissions
|
||||
.. _user authentication docs: ../authentication/#users
|
||||
.. _message docs: ../authentication/#messages
|
||||
.. _permissions docs: ../authentication/#permissions
|
||||
|
||||
django.core.context_processors.debug
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -383,9 +383,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
||||
|
||||
See the `internationalization docs`_ for more.
|
||||
|
||||
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
|
||||
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code
|
||||
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/
|
||||
.. _LANGUAGES setting: ../settings/#languages
|
||||
.. _LANGUAGE_CODE setting: ../settings/#language-code
|
||||
.. _internationalization docs: ../i18n/
|
||||
|
||||
django.core.context_processors.request
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -944,7 +944,7 @@ The ``takes_context`` parameter defaults to ``False``. When it's set to *True*,
|
||||
the tag is passed the context object, as in this example. That's the only
|
||||
difference between this case and the previous ``inclusion_tag`` example.
|
||||
|
||||
.. _tutorials: http://www.djangoproject.com/documentation/tutorial1/#creating-models
|
||||
.. _tutorials: ../tutorial1/#creating-models
|
||||
|
||||
Setting a variable in the context
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -1115,5 +1115,5 @@ settings you wish to specify. You might want to consider setting at least
|
||||
`settings documentation`_, and any setting starting with *TEMPLATE_*
|
||||
is of obvious interest.
|
||||
|
||||
.. _settings file: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||
.. _settings documentation: http://www.djangoproject.com/documentation/settings/
|
||||
.. _settings file: ../settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||
.. _settings documentation: ../settings/
|
||||
|
@ -13,7 +13,7 @@ changed unexpectedly as a result of the refactor.
|
||||
Testing a web application is a complex task, as there are many
|
||||
components of a web application that must be validated and tested. To
|
||||
help you test your application, Django provides a test execution
|
||||
framework, and range of utilities that can be used to stimulate and
|
||||
framework, and range of utilities that can be used to simulate and
|
||||
inspect various facets of a web application.
|
||||
|
||||
This testing framework is currently under development, and may change
|
||||
@ -220,7 +220,7 @@ can be invoked on the ``Client`` instance.
|
||||
|
||||
will result in the evaluation of a GET request equivalent to::
|
||||
|
||||
http://yoursite.com/customers/details/?name='fred'&age=7
|
||||
http://yoursite.com/customers/details/?name=fred&age=7
|
||||
|
||||
``post(path, data={})``
|
||||
Make a POST request on the provided ``path``. The key-value pairs in the
|
||||
@ -244,11 +244,11 @@ can be invoked on the ``Client`` instance.
|
||||
|
||||
``login(path, username, password)``
|
||||
In a production site, it is likely that some views will be protected with
|
||||
the @login_required URL provided by ``django.contrib.auth``. Interacting
|
||||
the @login_required decorator provided by ``django.contrib.auth``. Interacting
|
||||
with a URL that has been login protected is a slightly complex operation,
|
||||
so the Test Client provides a simple URL to automate the login process. A
|
||||
so the Test Client provides a simple method to automate the login process. A
|
||||
call to ``login()`` stimulates the series of GET and POST calls required
|
||||
to log a user into a @login_required protected URL.
|
||||
to log a user into a @login_required protected view.
|
||||
|
||||
If login is possible, the final return value of ``login()`` is the response
|
||||
that is generated by issuing a GET request on the protected URL. If login
|
||||
@ -415,7 +415,7 @@ arguments:
|
||||
tested. This is the same format returned by ``django.db.models.get_apps()``
|
||||
|
||||
Verbosity determines the amount of notification and debug information that
|
||||
will be printed to the console; '0' is no output, '1' is normal output,
|
||||
will be printed to the console; `0` is no output, `1` is normal output,
|
||||
and `2` is verbose output.
|
||||
|
||||
Testing utilities
|
||||
|
@ -17,7 +17,7 @@ installed by running the Python interactive interpreter and typing
|
||||
``import django``. If that command runs successfully, with no errors, Django is
|
||||
installed.
|
||||
|
||||
.. _`Django installed`: http://www.djangoproject.com/documentation/install/
|
||||
.. _`Django installed`: ../install/
|
||||
|
||||
Creating a project
|
||||
==================
|
||||
@ -108,7 +108,7 @@ It worked!
|
||||
|
||||
Full docs for the development server are at `django-admin documentation`_.
|
||||
|
||||
.. _django-admin documentation: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _django-admin documentation: ../django_admin/
|
||||
|
||||
Database setup
|
||||
--------------
|
||||
@ -378,7 +378,7 @@ as you like, and it will only ever create the tables that don't exist.
|
||||
Read the `django-admin.py documentation`_ for full information on what the
|
||||
``manage.py`` utility can do.
|
||||
|
||||
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/
|
||||
.. _django-admin.py documentation: ../django_admin/
|
||||
|
||||
Playing with the API
|
||||
====================
|
||||
@ -555,5 +555,5 @@ For full details on the database API, see our `Database API reference`_.
|
||||
When you're comfortable with the API, read `part 2 of this tutorial`_ to get
|
||||
Django's automatic admin working.
|
||||
|
||||
.. _Database API reference: http://www.djangoproject.com/documentation/db_api/
|
||||
.. _part 2 of this tutorial: http://www.djangoproject.com/documentation/tutorial2/
|
||||
.. _Database API reference: ../db_api/
|
||||
.. _part 2 of this tutorial: ../tutorial2/
|
||||
|
@ -5,7 +5,7 @@ Writing your first Django app, part 2
|
||||
This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll
|
||||
application and will focus on Django's automatically-generated admin site.
|
||||
|
||||
.. _Tutorial 1: http://www.djangoproject.com/documentation/tutorial1/
|
||||
.. _Tutorial 1: ../tutorial1/
|
||||
|
||||
.. admonition:: Philosophy
|
||||
|
||||
@ -64,7 +64,7 @@ tutorial, remember?) You should see the Django admin index page:
|
||||
By default, you should see two types of editable content: groups and users.
|
||||
These are core features Django ships with by default.
|
||||
|
||||
.. _"I can't log in" questions: http://www.djangoproject.com/documentation/faq/#the-admin-site
|
||||
.. _"I can't log in" questions: ../faq/#the-admin-site
|
||||
|
||||
Make the poll app modifiable in the admin
|
||||
=========================================
|
||||
@ -402,7 +402,7 @@ Django automatically looks for a ``templates/`` subdirectory within each app
|
||||
package, for use as a fallback. See the `loader types documentation`_ for full
|
||||
information.
|
||||
|
||||
.. _loader types documentation: http://www.djangoproject.com/documentation/templates_python/#loader-types
|
||||
.. _loader types documentation: ../templates_python/#loader-types
|
||||
|
||||
Customize the admin index page
|
||||
==============================
|
||||
@ -433,5 +433,5 @@ general, see the `Django admin CSS guide`_.
|
||||
When you're comfortable with the admin site, read `part 3 of this tutorial`_ to
|
||||
start working on public poll views.
|
||||
|
||||
.. _Django admin CSS guide: http://www.djangoproject.com/documentation/admin_css/
|
||||
.. _part 3 of this tutorial: http://www.djangoproject.com/documentation/tutorial3/
|
||||
.. _Django admin CSS guide: ../admin_css/
|
||||
.. _part 3 of this tutorial: ../tutorial3/
|
||||
|
@ -5,7 +5,7 @@ Writing your first Django app, part 3
|
||||
This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
|
||||
application and will focus on creating the public interface -- "views."
|
||||
|
||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
||||
.. _Tutorial 2: ../tutorial2/
|
||||
|
||||
Philosophy
|
||||
==========
|
||||
@ -117,8 +117,8 @@ time the URLconf module is loaded. They're super fast.
|
||||
|
||||
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
|
||||
.. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
|
||||
.. _request and response documentation: http://www.djangoproject.com/documentation/request_response/
|
||||
.. _URLconf documentation: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
.. _request and response documentation: ../request_response/
|
||||
.. _URLconf documentation: ../url_dispatch/
|
||||
|
||||
Write your first view
|
||||
=====================
|
||||
@ -260,8 +260,7 @@ provides a shortcut. Here's the full ``index()`` view, rewritten::
|
||||
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
|
||||
return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
|
||||
|
||||
Note that we no longer need to import ``loader``, ``Context`` or
|
||||
``HttpResponse``.
|
||||
Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
|
||||
|
||||
The ``render_to_response()`` function takes a template name as its first
|
||||
argument and a dictionary as its optional second argument. It returns an
|
||||
@ -377,7 +376,7 @@ iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
|
||||
|
||||
See the `template guide`_ for full details on how templates work.
|
||||
|
||||
.. _template guide: http://www.djangoproject.com/documentation/templates/
|
||||
.. _template guide: ../templates/
|
||||
|
||||
Simplifying the URLconfs
|
||||
========================
|
||||
@ -464,4 +463,4 @@ All the poll app cares about is its relative URLs, not its absolute URLs.
|
||||
When you're comfortable with writing views, read `part 4 of this tutorial`_ to
|
||||
learn about simple form processing and generic views.
|
||||
|
||||
.. _part 4 of this tutorial: http://www.djangoproject.com/documentation/tutorial4/
|
||||
.. _part 4 of this tutorial: ../tutorial4/
|
||||
|
@ -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
|
||||
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
|
||||
======================================
|
||||
@ -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`_.
|
||||
|
||||
.. _generic views documentation: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _generic views documentation: ../generic_views/
|
||||
|
||||
Coming soon
|
||||
===========
|
||||
@ -241,4 +241,4 @@ installments:
|
||||
* Advanced admin features: Permissions
|
||||
* Advanced admin features: Custom JavaScript
|
||||
|
||||
.. _Tutorial 3: http://www.djangoproject.com/documentation/tutorial3/
|
||||
.. _Tutorial 3: ../tutorial3/
|
||||
|
@ -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
|
||||
regex as remaining arguments.
|
||||
|
||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
||||
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
||||
.. _settings file: ../settings/
|
||||
.. _request object: ../request_response/#httprequest-objects
|
||||
|
||||
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
|
||||
that in automatically.
|
||||
|
||||
.. _Django overview: http://www.djangoproject.com/documentation/overview/
|
||||
.. _Django overview: ../overview/
|
||||
|
||||
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`_
|
||||
to pass metadata and options to views.
|
||||
|
||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _generic views: ../generic_views/
|
||||
.. _syndication framework: ../syndication/
|
||||
|
||||
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
|
||||
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
|
||||
===========================================
|
||||
|
||||
**New in the Django development version.**
|
||||
**New in Django development version.**
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
``db_column`` parameter. Note that you'll use the field's name, not its column
|
||||
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
|
||||
|
||||
class Person(models.Model):
|
||||
class Author(models.Model):
|
||||
first_name = models.CharField(maxlength=30, db_column='firstname')
|
||||
last_name = models.CharField(maxlength=30, db_column='last')
|
||||
|
||||
def __str__(self):
|
||||
return '%s %s' % (self.first_name, self.last_name)
|
||||
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a Person.
|
||||
>>> p = Person(first_name='John', last_name='Smith')
|
||||
>>> p.save()
|
||||
class Meta:
|
||||
db_table = 'my_author_table'
|
||||
ordering = ('last_name','first_name')
|
||||
|
||||
>>> 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
|
||||
|
||||
>>> Person.objects.all()
|
||||
[<Person: John Smith>]
|
||||
# Create another author
|
||||
>>> a2 = Author(first_name='Peter', last_name='Jones')
|
||||
>>> a2.save()
|
||||
|
||||
>>> Person.objects.filter(first_name__exact='John')
|
||||
[<Person: John Smith>]
|
||||
# Create an article
|
||||
>>> art = Article(headline='Django lets you build web apps easily')
|
||||
>>> art.save()
|
||||
>>> art.authors = [a, a2]
|
||||
|
||||
>>> Person.objects.get(first_name__exact='John')
|
||||
<Person: John Smith>
|
||||
# Although the table and column names on Author have been set to
|
||||
# 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):
|
||||
...
|
||||
TypeError: Cannot resolve keyword 'firstname' into field
|
||||
|
||||
>>> p = Person.objects.get(last_name__exact='Smith')
|
||||
>>> p.first_name
|
||||
>>> a = Author.objects.get(last_name__exact='Smith')
|
||||
>>> a.first_name
|
||||
'John'
|
||||
>>> p.last_name
|
||||
>>> a.last_name
|
||||
'Smith'
|
||||
>>> p.firstname
|
||||
>>> a.firstname
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'Person' object has no attribute 'firstname'
|
||||
>>> p.last
|
||||
AttributeError: 'Author' object has no attribute 'firstname'
|
||||
>>> a.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>]
|
||||
|
||||
"""}
|
||||
|
@ -65,14 +65,14 @@ __test__ = {'API_TESTS':"""
|
||||
|
||||
# Objects with declared GenericRelations can be tagged directly -- the 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")
|
||||
<TaggedItem: fatty>
|
||||
>>> bacon.tags.create(tag="salty")
|
||||
<TaggedItem: salty>
|
||||
>>> lion.tags.create(tag="yellow")
|
||||
<TaggedItem: yellow>
|
||||
>>> lion.tags.create(tag="hairy")
|
||||
<TaggedItem: hairy>
|
||||
|
||||
>>> lion.tags.all()
|
||||
[<TaggedItem: hairy>, <TaggedItem: yellow>]
|
||||
@ -105,4 +105,30 @@ __test__ = {'API_TESTS':"""
|
||||
[<TaggedItem: shiny>]
|
||||
>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
|
||||
[<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)]
|
||||
|
||||
"""}
|
||||
|
@ -191,4 +191,19 @@ DoesNotExist: Article matching query does not exist.
|
||||
>>> Article.objects.filter(headline__contains='\\')
|
||||
[<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>]
|
||||
|
||||
"""}
|
||||
|
@ -203,7 +203,19 @@ __test__ = {'API_TESTS':"""
|
||||
>>> p2.article_set.all()
|
||||
[<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.save()
|
||||
>>> a2 = Article(id=None, headline='NASA uses Python')
|
||||
|
@ -6,17 +6,20 @@ model instance.
|
||||
|
||||
The function django.newforms.form_for_model() takes a model class and returns
|
||||
a Form that is tied to the model. This Form works just like any other Form,
|
||||
with one additional method: create(). The create() method creates an instance
|
||||
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
|
||||
the database if create(save=True), which is default. If you pass
|
||||
create(save=False), then you'll get the object without saving it.
|
||||
the database if save(commit=True), which is default. If you pass
|
||||
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
|
||||
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()
|
||||
method updates the model instance. It saves the changes to the database if
|
||||
apply_changes(save=True), which is default. If you pass save=False, then you'll
|
||||
get the object without saving it.
|
||||
other Form, with one additional method: save(). The save()
|
||||
method updates the model instance. It also takes a commit=True parameter.
|
||||
|
||||
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
|
||||
@ -29,7 +32,7 @@ class Category(models.Model):
|
||||
return self.name
|
||||
|
||||
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):
|
||||
return self.name
|
||||
@ -38,13 +41,14 @@ class Article(models.Model):
|
||||
headline = models.CharField(maxlength=50)
|
||||
pub_date = models.DateField()
|
||||
writer = models.ForeignKey(Writer)
|
||||
article = models.TextField()
|
||||
categories = models.ManyToManyField(Category, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
__test__ = {'API_TESTS': """
|
||||
>>> from django.newforms import form_for_model, form_for_instance, BaseForm
|
||||
>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
|
||||
>>> import datetime
|
||||
|
||||
>>> Category.objects.all()
|
||||
@ -67,35 +71,36 @@ __test__ = {'API_TESTS': """
|
||||
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
|
||||
|
||||
>>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
|
||||
>>> f.errors
|
||||
{}
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.clean_data
|
||||
{'url': u'entertainment', 'name': u'Entertainment'}
|
||||
>>> obj = f.create()
|
||||
>>> obj = f.save()
|
||||
>>> obj
|
||||
<Category: Entertainment>
|
||||
>>> Category.objects.all()
|
||||
[<Category: Entertainment>]
|
||||
|
||||
>>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
|
||||
>>> f.errors
|
||||
{}
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.clean_data
|
||||
{'url': u'test', 'name': u"It's a test"}
|
||||
>>> obj = f.create()
|
||||
>>> obj = f.save()
|
||||
>>> obj
|
||||
<Category: It's a test>
|
||||
>>> Category.objects.all()
|
||||
[<Category: Entertainment>, <Category: It's a test>]
|
||||
|
||||
If you call create() with save=False, then it will return an object that hasn't
|
||||
yet been saved. In this case, it's up to you to save it.
|
||||
If you call save() with commit=False, then it will return an object that
|
||||
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.errors
|
||||
{}
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.clean_data
|
||||
{'url': u'third', 'name': u'Third test'}
|
||||
>>> obj = f.create(save=False)
|
||||
>>> obj = f.save(commit=False)
|
||||
>>> obj
|
||||
<Category: Third test>
|
||||
>>> 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: 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.errors
|
||||
{'name': [u'This field is required.']}
|
||||
>>> 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):
|
||||
...
|
||||
ValueError: The Category could not be created because the data didn't validate.
|
||||
>>> f = CategoryForm({'name': '', 'url': 'foo'})
|
||||
>>> f.create()
|
||||
>>> f.save()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
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="2">Bob Woodward</option>
|
||||
</select></td></tr>
|
||||
<tr><th>Article:</th><td><textarea name="article"></textarea></td></tr>
|
||||
<tr><th>Categories:</th><td><select multiple="multiple" name="categories">
|
||||
<option value="1">Entertainment</option>
|
||||
<option value="2">It's a test</option>
|
||||
<option value="3">Third test</option>
|
||||
</select></td></tr>
|
||||
</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
|
||||
subclass of BaseForm, not Form.
|
||||
@ -153,17 +162,16 @@ subclass of BaseForm, not Form.
|
||||
>>> f.say_hello()
|
||||
hello
|
||||
|
||||
Use form_for_instance to create a Form from a model instance. There are two
|
||||
differences between this Form and one created via form_for_model. First, the
|
||||
object's current values are inserted as 'initial' data in each Field. Second,
|
||||
the Form gets an apply_changes() method instead of a create() method.
|
||||
Use form_for_instance to create a Form from a model instance. The difference
|
||||
between this Form and one created via form_for_model is that the object's
|
||||
current values are inserted as 'initial' data in each Field.
|
||||
>>> w = Writer.objects.get(name='Mike Royko')
|
||||
>>> RoykoForm = form_for_instance(w)
|
||||
>>> f = RoykoForm(auto_id=False)
|
||||
>>> print f
|
||||
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /></td></tr>
|
||||
<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.id
|
||||
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="2">Bob Woodward</option>
|
||||
</select></li>
|
||||
<li>Article: <textarea name="article">Hello.</textarea></li>
|
||||
<li>Categories: <select multiple="multiple" name="categories">
|
||||
<option value="1">Entertainment</option>
|
||||
<option value="2">It's a test</option>
|
||||
<option value="3">Third test</option>
|
||||
</select></li>
|
||||
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1'})
|
||||
</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': 'Hello.'})
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> new_art = f.apply_changes()
|
||||
>>> new_art = f.save()
|
||||
>>> new_art.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="2">Bob Woodward</option>
|
||||
</select></li>
|
||||
<li>Article: <textarea name="article">Hello.</textarea></li>
|
||||
<li>Categories: <select multiple="multiple" name="categories">
|
||||
<option value="1" selected="selected">Entertainment</option>
|
||||
<option value="2">It's a 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>
|
||||
"""}
|
||||
|
@ -69,6 +69,21 @@ __test__ = {'API_TESTS':"""
|
||||
>>> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))
|
||||
[<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
|
||||
>>> Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye'))
|
||||
[<Article: Hello and goodbye>]
|
||||
|
@ -37,6 +37,13 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
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':"""
|
||||
# Create some data:
|
||||
>>> from datetime import datetime
|
||||
@ -118,4 +125,18 @@ __test__ = {'API_TESTS':"""
|
||||
>>> Article.objects.all()
|
||||
[<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>
|
||||
|
||||
"""}
|
||||
|
@ -26,10 +26,10 @@ def redirect_view(request):
|
||||
"A view that redirects all requests to the GET view"
|
||||
return HttpResponseRedirect('/test_client/get_view/')
|
||||
|
||||
@login_required
|
||||
def login_protected_view(request):
|
||||
"A simple view that is login protected."
|
||||
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
|
||||
c = Context({'user': request.user})
|
||||
|
||||
return HttpResponse(t.render(c))
|
||||
login_protected_view = login_required(login_protected_view)
|
@ -106,6 +106,46 @@ u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u
|
||||
>>> w.render('email', '', attrs={'class': 'special'})
|
||||
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 "quoted" & 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 ############################################################
|
||||
|
||||
>>> 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ćžšđ')])
|
||||
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 #######################################################
|
||||
|
||||
>>> w = SelectMultiple()
|
||||
@ -527,12 +621,16 @@ True
|
||||
>>> r[1].is_checked()
|
||||
False
|
||||
>>> 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]
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
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 ###############################################
|
||||
|
||||
>>> 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ćžšđ')])
|
||||
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 #
|
||||
##########
|
||||
@ -766,9 +897,11 @@ ValidationError: [u'Enter a whole number.']
|
||||
|
||||
>>> f = IntegerField(required=False)
|
||||
>>> f.clean('')
|
||||
u''
|
||||
>>> repr(f.clean(''))
|
||||
'None'
|
||||
>>> f.clean(None)
|
||||
u''
|
||||
>>> repr(f.clean(None))
|
||||
'None'
|
||||
>>> f.clean('1')
|
||||
1
|
||||
>>> isinstance(f.clean('1'), int)
|
||||
@ -1285,6 +1418,11 @@ ValidationError: [u'This URL appears to be a broken link.']
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
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.
|
||||
>>> 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.']
|
||||
|
||||
# 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 #########################################################
|
||||
|
||||
>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')])
|
||||
@ -1485,6 +1637,58 @@ u''
|
||||
>>> f.clean(None)
|
||||
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 #
|
||||
#########
|
||||
@ -1502,6 +1706,8 @@ You can pass it data in __init__(), as a dictionary.
|
||||
|
||||
Pass a dictionary to a Form's __init__().
|
||||
>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
|
||||
>>> p.is_bound
|
||||
True
|
||||
>>> p.errors
|
||||
{}
|
||||
>>> p.is_valid()
|
||||
@ -1540,10 +1746,16 @@ Birthday 1940-10-9
|
||||
|
||||
Empty dictionaries are valid, too.
|
||||
>>> p = Person({})
|
||||
>>> p.is_bound
|
||||
True
|
||||
>>> p.errors
|
||||
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
|
||||
>>> p.is_valid()
|
||||
False
|
||||
>>> p.clean_data
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'Person' object has no attribute 'clean_data'
|
||||
>>> 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_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>
|
||||
|
||||
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*
|
||||
Form.is_valid() will return False.
|
||||
the Form will be considered unbound and won't do any validation. Form.errors
|
||||
will be an empty dictionary *but* Form.is_valid() will return False.
|
||||
>>> p = Person()
|
||||
>>> p.is_bound
|
||||
False
|
||||
>>> p.errors
|
||||
{}
|
||||
>>> p.is_valid()
|
||||
False
|
||||
>>> p.clean_data
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'Person' object has no attribute 'clean_data'
|
||||
>>> 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_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
|
||||
* This field is required.
|
||||
>>> p.clean_data
|
||||
>>> repr(p.clean_data)
|
||||
'None'
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'Person' object has no attribute 'clean_data'
|
||||
>>> p['first_name'].errors
|
||||
[u'This field is required.']
|
||||
>>> p['first_name'].errors.as_ul()
|
||||
@ -1628,6 +1847,17 @@ u'* This field is required.'
|
||||
>>> print p['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.
|
||||
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
|
||||
@ -1753,6 +1983,57 @@ For a form with a <select>, use ChoiceField:
|
||||
<option value="J">Java</option>
|
||||
</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.
|
||||
>>> class FrameworkForm(Form):
|
||||
... 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>
|
||||
</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.
|
||||
>>> class SongForm(Form):
|
||||
... 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>
|
||||
</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
|
||||
conveniently work with this.
|
||||
>>> data = {'name': 'Yesterday', 'composers': ['J', 'P']}
|
||||
@ -1869,11 +2171,20 @@ conveniently work with this.
|
||||
>>> f.errors
|
||||
{}
|
||||
>>> 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.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
|
||||
returns a list of input.
|
||||
>>> f = SongForm({'name': 'Yesterday'}, auto_id=False)
|
||||
@ -1890,6 +2201,8 @@ returns a list of input.
|
||||
>>> f.clean_data
|
||||
{'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
|
||||
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
|
||||
@ -1964,6 +2277,8 @@ Form.clean() is required to return a dictionary of all clean data.
|
||||
>>> f.clean_data
|
||||
{'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
|
||||
dictionary in __init__(). Don't forget to call Form.__init__() within the
|
||||
subclass' __init__().
|
||||
@ -1979,6 +2294,46 @@ subclass' __init__().
|
||||
<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>
|
||||
|
||||
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()
|
||||
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
|
||||
@ -2200,6 +2555,96 @@ validation error rather than using the initial value for 'username'.
|
||||
>>> p.is_valid()
|
||||
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 #########################################################
|
||||
|
||||
Sometimes it's necessary to have multiple forms display on the same HTML page,
|
||||
@ -2311,6 +2756,57 @@ True
|
||||
>>> p.clean_data
|
||||
{'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 #############################################
|
||||
|
||||
>>> from django.template import Template, Context
|
||||
@ -2439,6 +2935,15 @@ field an "id" attribute.
|
||||
<input type="submit" />
|
||||
</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.,
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user