mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
boulder-oracle-sprint: Merged to trunk [4455].
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@4456 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
92b7851424
commit
e17f755514
@ -25,7 +25,7 @@
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<p>{% blocktrans with original.username|escape as username %}Enter a new username and password for the user <strong>{{ username }}</strong>.{% endblocktrans %}</p>
|
<p>{% blocktrans with original.username|escape as username %}Enter a new password for the user <strong>{{ username }}</strong>.{% endblocktrans %}</p>
|
||||||
|
|
||||||
<fieldset class="module aligned">
|
<fieldset class="module aligned">
|
||||||
|
|
||||||
|
@ -101,6 +101,10 @@ def result_headers(cl):
|
|||||||
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
||||||
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
|
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
|
||||||
|
|
||||||
|
def _boolean_icon(field_val):
|
||||||
|
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||||
|
return '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
||||||
|
|
||||||
def items_for_result(cl, result):
|
def items_for_result(cl, result):
|
||||||
first = True
|
first = True
|
||||||
pk = cl.lookup_opts.pk.attname
|
pk = cl.lookup_opts.pk.attname
|
||||||
@ -114,9 +118,14 @@ def items_for_result(cl, result):
|
|||||||
try:
|
try:
|
||||||
attr = getattr(result, field_name)
|
attr = getattr(result, field_name)
|
||||||
allow_tags = getattr(attr, 'allow_tags', False)
|
allow_tags = getattr(attr, 'allow_tags', False)
|
||||||
|
boolean = getattr(attr, 'boolean', False)
|
||||||
if callable(attr):
|
if callable(attr):
|
||||||
attr = attr()
|
attr = attr()
|
||||||
result_repr = str(attr)
|
if boolean:
|
||||||
|
allow_tags = True
|
||||||
|
result_repr = _boolean_icon(attr)
|
||||||
|
else:
|
||||||
|
result_repr = str(attr)
|
||||||
except (AttributeError, ObjectDoesNotExist):
|
except (AttributeError, ObjectDoesNotExist):
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
else:
|
else:
|
||||||
@ -147,8 +156,7 @@ def items_for_result(cl, result):
|
|||||||
row_class = ' class="nowrap"'
|
row_class = ' class="nowrap"'
|
||||||
# Booleans are special: We use images.
|
# Booleans are special: We use images.
|
||||||
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
|
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
|
||||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
result_repr = _boolean_icon(field_val)
|
||||||
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
|
||||||
# FloatFields are special: Zero-pad the decimals.
|
# FloatFields are special: Zero-pad the decimals.
|
||||||
elif isinstance(f, models.FloatField):
|
elif isinstance(f, models.FloatField):
|
||||||
if field_val is not None:
|
if field_val is not None:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
CONTENT_TYPE_CACHE = {}
|
||||||
class ContentTypeManager(models.Manager):
|
class ContentTypeManager(models.Manager):
|
||||||
def get_for_model(self, model):
|
def get_for_model(self, model):
|
||||||
"""
|
"""
|
||||||
@ -8,10 +9,15 @@ class ContentTypeManager(models.Manager):
|
|||||||
ContentType if necessary.
|
ContentType if necessary.
|
||||||
"""
|
"""
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
# The str() is needed around opts.verbose_name because it's a
|
key = (opts.app_label, opts.object_name.lower())
|
||||||
# django.utils.functional.__proxy__ object.
|
try:
|
||||||
ct, created = self.model._default_manager.get_or_create(app_label=opts.app_label,
|
ct = CONTENT_TYPE_CACHE[key]
|
||||||
model=opts.object_name.lower(), defaults={'name': str(opts.verbose_name)})
|
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
|
return ct
|
||||||
|
|
||||||
class ContentType(models.Model):
|
class ContentType(models.Model):
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sessions.models import Session
|
from django.contrib.sessions.models import Session
|
||||||
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ class SessionWrapper(object):
|
|||||||
s = Session.objects.get(session_key=self.session_key,
|
s = Session.objects.get(session_key=self.session_key,
|
||||||
expire_date__gt=datetime.datetime.now())
|
expire_date__gt=datetime.datetime.now())
|
||||||
self._session_cache = s.get_decoded()
|
self._session_cache = s.get_decoded()
|
||||||
except Session.DoesNotExist:
|
except (Session.DoesNotExist, SuspiciousOperation):
|
||||||
self._session_cache = {}
|
self._session_cache = {}
|
||||||
# Set the session_key to None to force creation of a new
|
# Set the session_key to None to force creation of a new
|
||||||
# key, for extra security.
|
# key, for extra security.
|
||||||
|
6
django/core/cache/backends/dummy.py
vendored
6
django/core/cache/backends/dummy.py
vendored
@ -6,8 +6,8 @@ class CacheClass(BaseCache):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, key, default=None):
|
||||||
pass
|
return default
|
||||||
|
|
||||||
def set(self, *args, **kwargs):
|
def set(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
@ -16,7 +16,7 @@ class CacheClass(BaseCache):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def get_many(self, *args, **kwargs):
|
def get_many(self, *args, **kwargs):
|
||||||
pass
|
return {}
|
||||||
|
|
||||||
def has_key(self, *args, **kwargs):
|
def has_key(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
@ -25,7 +25,7 @@ APP_ARGS = '[appname ...]'
|
|||||||
# which has been installed.
|
# which has been installed.
|
||||||
PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
|
PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
|
||||||
|
|
||||||
INVALID_PROJECT_NAMES = ('django', 'test')
|
INVALID_PROJECT_NAMES = ('django', 'site', 'test')
|
||||||
|
|
||||||
# Set up the terminal color scheme.
|
# Set up the terminal color scheme.
|
||||||
class dummy: pass
|
class dummy: pass
|
||||||
@ -735,7 +735,7 @@ def startproject(project_name, directory):
|
|||||||
"Creates a Django project for the given project_name in the given directory."
|
"Creates a Django project for the given project_name in the given directory."
|
||||||
from random import choice
|
from random import choice
|
||||||
if project_name in INVALID_PROJECT_NAMES:
|
if project_name in INVALID_PROJECT_NAMES:
|
||||||
sys.stderr.write(style.ERROR("Error: %r isn't a valid project name. Please try another.\n" % project_name))
|
sys.stderr.write(style.ERROR("Error: '%r' conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name.\n" % project_name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
_start_helper('project', project_name, directory)
|
_start_helper('project', project_name, directory)
|
||||||
# Create a random SECRET_KEY hash, and put it in the main settings.
|
# Create a random SECRET_KEY hash, and put it in the main settings.
|
||||||
|
@ -57,7 +57,7 @@ def Deserializer(object_list, **options):
|
|||||||
for d in object_list:
|
for d in object_list:
|
||||||
# Look up the model and starting build a dict of data for it.
|
# Look up the model and starting build a dict of data for it.
|
||||||
Model = _get_model(d["model"])
|
Model = _get_model(d["model"])
|
||||||
data = {Model._meta.pk.name : d["pk"]}
|
data = {Model._meta.pk.attname : d["pk"]}
|
||||||
m2m_data = {}
|
m2m_data = {}
|
||||||
|
|
||||||
# Handle each field
|
# Handle each field
|
||||||
|
@ -21,6 +21,5 @@ DATA_TYPES = {
|
|||||||
'SmallIntegerField': 'smallint',
|
'SmallIntegerField': 'smallint',
|
||||||
'TextField': 'text',
|
'TextField': 'text',
|
||||||
'TimeField': 'time',
|
'TimeField': 'time',
|
||||||
'URLField': 'varchar(200)',
|
|
||||||
'USStateField': 'varchar(2)',
|
'USStateField': 'varchar(2)',
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,5 @@ DATA_TYPES = {
|
|||||||
'SmallIntegerField': 'smallint',
|
'SmallIntegerField': 'smallint',
|
||||||
'TextField': 'longtext',
|
'TextField': 'longtext',
|
||||||
'TimeField': 'time',
|
'TimeField': 'time',
|
||||||
'URLField': 'varchar(200)',
|
|
||||||
'USStateField': 'varchar(2)',
|
'USStateField': 'varchar(2)',
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,5 @@ DATA_TYPES = {
|
|||||||
'SmallIntegerField': 'smallint',
|
'SmallIntegerField': 'smallint',
|
||||||
'TextField': 'text',
|
'TextField': 'text',
|
||||||
'TimeField': 'time',
|
'TimeField': 'time',
|
||||||
'URLField': 'varchar(200)',
|
|
||||||
'USStateField': 'varchar(2)',
|
'USStateField': 'varchar(2)',
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,5 @@ DATA_TYPES = {
|
|||||||
'SmallIntegerField': 'smallint',
|
'SmallIntegerField': 'smallint',
|
||||||
'TextField': 'text',
|
'TextField': 'text',
|
||||||
'TimeField': 'time',
|
'TimeField': 'time',
|
||||||
'URLField': 'varchar(200)',
|
|
||||||
'USStateField': 'varchar(2)',
|
'USStateField': 'varchar(2)',
|
||||||
}
|
}
|
||||||
|
@ -337,11 +337,11 @@ class Field(object):
|
|||||||
return self._choices
|
return self._choices
|
||||||
choices = property(_get_choices)
|
choices = property(_get_choices)
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
"Returns a django.newforms.Field instance for this database Field."
|
"Returns a django.newforms.Field instance for this database Field."
|
||||||
from django.newforms import CharField
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
# TODO: This is just a temporary default during development.
|
defaults.update(kwargs)
|
||||||
return forms.CharField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
return forms.CharField(**defaults)
|
||||||
|
|
||||||
def value_from_object(self, obj):
|
def value_from_object(self, obj):
|
||||||
"Returns the value of this field in the given model instance."
|
"Returns the value of this field in the given model instance."
|
||||||
@ -383,7 +383,7 @@ class AutoField(Field):
|
|||||||
super(AutoField, self).contribute_to_class(cls, name)
|
super(AutoField, self).contribute_to_class(cls, name)
|
||||||
cls._meta.has_auto_field = True
|
cls._meta.has_auto_field = True
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
class BooleanField(Field):
|
class BooleanField(Field):
|
||||||
@ -400,8 +400,10 @@ class BooleanField(Field):
|
|||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [oldforms.CheckboxField]
|
return [oldforms.CheckboxField]
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return forms.BooleanField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.BooleanField(**defaults)
|
||||||
|
|
||||||
class CharField(Field):
|
class CharField(Field):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
@ -417,8 +419,10 @@ class CharField(Field):
|
|||||||
raise validators.ValidationError, gettext_lazy("This field cannot be null.")
|
raise validators.ValidationError, gettext_lazy("This field cannot be null.")
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return forms.CharField(max_length=self.maxlength, required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'max_length': self.maxlength, 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.CharField(**defaults)
|
||||||
|
|
||||||
# TODO: Maybe move this into contrib, because it's specialized.
|
# TODO: Maybe move this into contrib, because it's specialized.
|
||||||
class CommaSeparatedIntegerField(CharField):
|
class CommaSeparatedIntegerField(CharField):
|
||||||
@ -497,8 +501,10 @@ class DateField(Field):
|
|||||||
val = self._get_val_from_obj(obj)
|
val = self._get_val_from_obj(obj)
|
||||||
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
|
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return forms.DateField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.DateField(**defaults)
|
||||||
|
|
||||||
class DateTimeField(DateField):
|
class DateTimeField(DateField):
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
@ -569,8 +575,10 @@ class DateTimeField(DateField):
|
|||||||
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
|
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
|
||||||
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
|
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return forms.DateTimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.DateTimeField(**defaults)
|
||||||
|
|
||||||
class EmailField(CharField):
|
class EmailField(CharField):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -586,8 +594,10 @@ class EmailField(CharField):
|
|||||||
def validate(self, field_data, all_data):
|
def validate(self, field_data, all_data):
|
||||||
validators.isValidEmail(field_data, all_data)
|
validators.isValidEmail(field_data, all_data)
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return forms.EmailField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.EmailField(**defaults)
|
||||||
|
|
||||||
class FileField(Field):
|
class FileField(Field):
|
||||||
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
|
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
|
||||||
@ -721,8 +731,10 @@ class IntegerField(Field):
|
|||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [oldforms.IntegerField]
|
return [oldforms.IntegerField]
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return forms.IntegerField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.IntegerField(**defaults)
|
||||||
|
|
||||||
class IPAddressField(Field):
|
class IPAddressField(Field):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -778,6 +790,11 @@ class TextField(Field):
|
|||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [oldforms.LargeTextField]
|
return [oldforms.LargeTextField]
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'widget': forms.Textarea, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.CharField(**defaults)
|
||||||
|
|
||||||
class TimeField(Field):
|
class TimeField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
|
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
|
||||||
@ -824,21 +841,29 @@ class TimeField(Field):
|
|||||||
val = self._get_val_from_obj(obj)
|
val = self._get_val_from_obj(obj)
|
||||||
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
|
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return forms.TimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.TimeField(**defaults)
|
||||||
|
|
||||||
class URLField(Field):
|
class URLField(CharField):
|
||||||
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
|
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
|
||||||
|
kwargs['maxlength'] = kwargs.get('maxlength', 200)
|
||||||
if verify_exists:
|
if verify_exists:
|
||||||
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
|
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
|
||||||
self.verify_exists = verify_exists
|
self.verify_exists = verify_exists
|
||||||
Field.__init__(self, verbose_name, name, **kwargs)
|
CharField.__init__(self, verbose_name, name, **kwargs)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [oldforms.URLField]
|
return [oldforms.URLField]
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def get_internal_type(self):
|
||||||
return forms.URLField(required=not self.blank, verify_exists=self.verify_exists, label=capfirst(self.verbose_name), initial=initial)
|
return "CharField"
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'required': not self.blank, 'verify_exists': self.verify_exists, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.URLField(**defaults)
|
||||||
|
|
||||||
class USStateField(Field):
|
class USStateField(Field):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
|
@ -316,18 +316,20 @@ def create_many_related_manager(superclass):
|
|||||||
# join_table: name of the m2m link table
|
# join_table: name of the m2m link table
|
||||||
# source_col_name: the PK colname in join_table for the source object
|
# source_col_name: the PK colname in join_table for the source object
|
||||||
# target_col_name: the PK colname in join_table for the target object
|
# target_col_name: the PK colname in join_table for the target object
|
||||||
# *objs - objects to add
|
# *objs - objects to add. Either object instances, or primary keys of object instances.
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
# If there aren't any objects, there is nothing to do.
|
# If there aren't any objects, there is nothing to do.
|
||||||
if objs:
|
if objs:
|
||||||
# Check that all the objects are of the right type
|
# Check that all the objects are of the right type
|
||||||
|
new_ids = set()
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
if not isinstance(obj, self.model):
|
if isinstance(obj, self.model):
|
||||||
raise ValueError, "objects to add() must be %s instances" % self.model._meta.object_name
|
new_ids.add(obj._get_pk_val())
|
||||||
|
else:
|
||||||
|
new_ids.add(obj)
|
||||||
# Add the newly created or already existing objects to the join table.
|
# Add the newly created or already existing objects to the join table.
|
||||||
# First find out which items are already added, to avoid adding them twice
|
# First find out which items are already added, to avoid adding them twice
|
||||||
new_ids = set([obj._get_pk_val() for obj in objs])
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||||
(target_col_name, self.join_table, source_col_name,
|
(target_col_name, self.join_table, source_col_name,
|
||||||
@ -354,11 +356,13 @@ def create_many_related_manager(superclass):
|
|||||||
# If there aren't any objects, there is nothing to do.
|
# If there aren't any objects, there is nothing to do.
|
||||||
if objs:
|
if objs:
|
||||||
# Check that all the objects are of the right type
|
# Check that all the objects are of the right type
|
||||||
|
old_ids = set()
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
if not isinstance(obj, self.model):
|
if isinstance(obj, self.model):
|
||||||
raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name
|
old_ids.add(obj._get_pk_val())
|
||||||
|
else:
|
||||||
|
old_ids.add(obj)
|
||||||
# Remove the specified objects from the join table
|
# Remove the specified objects from the join table
|
||||||
old_ids = set([obj._get_pk_val() for obj in objs])
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
|
||||||
(self.join_table, source_col_name,
|
(self.join_table, source_col_name,
|
||||||
@ -548,8 +552,10 @@ class ForeignKey(RelatedField, Field):
|
|||||||
def contribute_to_related_class(self, cls, related):
|
def contribute_to_related_class(self, cls, related):
|
||||||
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.ChoiceField(**defaults)
|
||||||
|
|
||||||
class OneToOneField(RelatedField, IntegerField):
|
class OneToOneField(RelatedField, IntegerField):
|
||||||
def __init__(self, to, to_field=None, **kwargs):
|
def __init__(self, to, to_field=None, **kwargs):
|
||||||
@ -612,8 +618,10 @@ class OneToOneField(RelatedField, IntegerField):
|
|||||||
if not cls._meta.one_to_one_field:
|
if not cls._meta.one_to_one_field:
|
||||||
cls._meta.one_to_one_field = self
|
cls._meta.one_to_one_field = self
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.ChoiceField(**kwargs)
|
||||||
|
|
||||||
class ManyToManyField(RelatedField, Field):
|
class ManyToManyField(RelatedField, Field):
|
||||||
def __init__(self, to, **kwargs):
|
def __init__(self, to, **kwargs):
|
||||||
@ -625,6 +633,7 @@ class ManyToManyField(RelatedField, Field):
|
|||||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||||
raw_id_admin=kwargs.pop('raw_id_admin', False),
|
raw_id_admin=kwargs.pop('raw_id_admin', False),
|
||||||
symmetrical=kwargs.pop('symmetrical', True))
|
symmetrical=kwargs.pop('symmetrical', True))
|
||||||
|
self.db_table = kwargs.pop('db_table', None)
|
||||||
if kwargs["rel"].raw_id_admin:
|
if kwargs["rel"].raw_id_admin:
|
||||||
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
|
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
|
||||||
Field.__init__(self, **kwargs)
|
Field.__init__(self, **kwargs)
|
||||||
@ -647,10 +656,10 @@ class ManyToManyField(RelatedField, Field):
|
|||||||
|
|
||||||
def _get_m2m_db_table(self, opts):
|
def _get_m2m_db_table(self, opts):
|
||||||
"Function that can be curried to provide the m2m table name for this relation"
|
"Function that can be curried to provide the m2m table name for this relation"
|
||||||
from django.db import backend
|
if self.db_table:
|
||||||
from django.db.backends.util import truncate_name
|
return self.db_table
|
||||||
name = '%s_%s' % (opts.db_table, self.name)
|
else:
|
||||||
return truncate_name(name, backend.get_max_name_length())
|
return '%s_%s' % (opts.db_table, self.name)
|
||||||
|
|
||||||
def _get_m2m_column_name(self, related):
|
def _get_m2m_column_name(self, related):
|
||||||
"Function that can be curried to provide the source column name for the m2m table"
|
"Function that can be curried to provide the source column name for the m2m table"
|
||||||
@ -728,12 +737,14 @@ class ManyToManyField(RelatedField, Field):
|
|||||||
"Returns the value of this field in the given model instance."
|
"Returns the value of this field in the given model instance."
|
||||||
return getattr(obj, self.attname).all()
|
return getattr(obj, self.attname).all()
|
||||||
|
|
||||||
def formfield(self, initial=None):
|
def formfield(self, **kwargs):
|
||||||
# If initial is passed in, it's a list of related objects, but the
|
# If initial is passed in, it's a list of related objects, but the
|
||||||
# MultipleChoiceField takes a list of IDs.
|
# MultipleChoiceField takes a list of IDs.
|
||||||
if initial is not None:
|
if kwargs.get('initial') is not None:
|
||||||
initial = [i._get_pk_val() for i in initial]
|
kwargs['initial'] = [i._get_pk_val() for i in kwargs['initial']]
|
||||||
return forms.MultipleChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
|
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return forms.MultipleChoiceField(**defaults)
|
||||||
|
|
||||||
class ManyToOneRel(object):
|
class ManyToOneRel(object):
|
||||||
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet, EmptyQuerySet
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.db.models.fields import FieldDoesNotExist
|
from django.db.models.fields import FieldDoesNotExist
|
||||||
@ -42,12 +42,18 @@ class Manager(object):
|
|||||||
# PROXIES TO QUERYSET #
|
# PROXIES TO QUERYSET #
|
||||||
#######################
|
#######################
|
||||||
|
|
||||||
|
def get_empty_query_set(self):
|
||||||
|
return EmptyQuerySet(self.model)
|
||||||
|
|
||||||
def get_query_set(self):
|
def get_query_set(self):
|
||||||
"""Returns a new QuerySet object. Subclasses can override this method
|
"""Returns a new QuerySet object. Subclasses can override this method
|
||||||
to easily customise the behaviour of the Manager.
|
to easily customise the behaviour of the Manager.
|
||||||
"""
|
"""
|
||||||
return QuerySet(self.model)
|
return QuerySet(self.model)
|
||||||
|
|
||||||
|
def none(self):
|
||||||
|
return self.get_empty_query_set()
|
||||||
|
|
||||||
def all(self):
|
def all(self):
|
||||||
return self.get_query_set()
|
return self.get_query_set()
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.db import backend, connection, get_query_module, transaction
|
from django.db import backend, connection, get_query_module, transaction
|
||||||
from django.db.models.fields import DateField, FieldDoesNotExist
|
from django.db.models.fields import DateField, FieldDoesNotExist
|
||||||
|
from django.db.models.fields.generic import GenericRelation
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
@ -25,6 +26,9 @@ QUERY_TERMS = (
|
|||||||
# Larger values are slightly faster at the expense of more storage space.
|
# Larger values are slightly faster at the expense of more storage space.
|
||||||
GET_ITERATOR_CHUNK_SIZE = 100
|
GET_ITERATOR_CHUNK_SIZE = 100
|
||||||
|
|
||||||
|
class EmptyResultSet(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# HELPER FUNCTIONS #
|
# HELPER FUNCTIONS #
|
||||||
####################
|
####################
|
||||||
@ -169,7 +173,11 @@ class _QuerySet(object):
|
|||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
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)
|
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||||
|
|
||||||
fill_cache = self._select_related
|
fill_cache = self._select_related
|
||||||
@ -194,7 +202,12 @@ class _QuerySet(object):
|
|||||||
counter._offset = None
|
counter._offset = None
|
||||||
counter._limit = None
|
counter._limit = None
|
||||||
counter._select_related = False
|
counter._select_related = False
|
||||||
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()
|
cursor = connection.cursor()
|
||||||
if self._distinct:
|
if self._distinct:
|
||||||
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
|
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
|
||||||
@ -532,7 +545,12 @@ class ValuesQuerySet(QuerySet):
|
|||||||
field_names = [f.attname for f in self.model._meta.fields]
|
field_names = [f.attname for f in self.model._meta.fields]
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
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]
|
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
|
||||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||||
while 1:
|
while 1:
|
||||||
@ -554,19 +572,25 @@ class DateQuerySet(QuerySet):
|
|||||||
if self._field.null:
|
if self._field.null:
|
||||||
self._where.append('%s.%s IS NOT NULL' % \
|
self._where.append('%s.%s IS NOT NULL' % \
|
||||||
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
|
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
|
||||||
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)
|
table_name = backend.quote_name(self.model._meta.db_table)
|
||||||
field_name = backend.quote_name(self._field.column)
|
field_name = backend.quote_name(self._field.column)
|
||||||
date_trunc_sql = backend.get_date_trunc_sql(self._kind,
|
|
||||||
'%s.%s' % (table_name, field_name))
|
|
||||||
if backend.allows_group_by_ordinal:
|
if backend.allows_group_by_ordinal:
|
||||||
group_by = '1'
|
group_by = '1'
|
||||||
else:
|
else:
|
||||||
group_by = date_trunc_sql
|
group_by = backend.get_date_trunc_sql(self._kind,
|
||||||
fmt = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s'
|
'%s.%s' % (table_name, field_name))
|
||||||
stmt = fmt % (date_trunc_sql, sql, group_by, self._order)
|
|
||||||
|
sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \
|
||||||
|
(backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
|
||||||
|
backend.quote_name(self._field.column))), sql, group_by, self._order)
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute(stmt, params)
|
cursor.execute(sql, params)
|
||||||
if backend.needs_datetime_string_cast:
|
if backend.needs_datetime_string_cast:
|
||||||
return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
|
return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
|
||||||
else:
|
else:
|
||||||
@ -579,6 +603,25 @@ class DateQuerySet(QuerySet):
|
|||||||
c._order = self._order
|
c._order = self._order
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
class EmptyQuerySet(QuerySet):
|
||||||
|
def __init__(self, model=None):
|
||||||
|
super(EmptyQuerySet, self).__init__(model)
|
||||||
|
self._result_cache = []
|
||||||
|
|
||||||
|
def iterator(self):
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _clone(self, klass=None, **kwargs):
|
||||||
|
c = super(EmptyQuerySet, self)._clone(klass, **kwargs)
|
||||||
|
c._result_cache = []
|
||||||
|
return c
|
||||||
|
|
||||||
class QOperator(object):
|
class QOperator(object):
|
||||||
"Base class for QAnd and QOr"
|
"Base class for QAnd and QOr"
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
@ -587,10 +630,14 @@ class QOperator(object):
|
|||||||
def get_sql(self, opts):
|
def get_sql(self, opts):
|
||||||
joins, where, params = SortedDict(), [], []
|
joins, where, params = SortedDict(), [], []
|
||||||
for val in self.args:
|
for val in self.args:
|
||||||
joins2, where2, params2 = val.get_sql(opts)
|
try:
|
||||||
joins.update(joins2)
|
joins2, where2, params2 = val.get_sql(opts)
|
||||||
where.extend(where2)
|
joins.update(joins2)
|
||||||
params.extend(params2)
|
where.extend(where2)
|
||||||
|
params.extend(params2)
|
||||||
|
except EmptyResultSet:
|
||||||
|
if not isinstance(self, QOr):
|
||||||
|
raise EmptyResultSet
|
||||||
if where:
|
if where:
|
||||||
return joins, ['(%s)' % self.operator.join(where)], params
|
return joins, ['(%s)' % self.operator.join(where)], params
|
||||||
return joins, [], params
|
return joins, [], params
|
||||||
@ -644,8 +691,11 @@ class QNot(Q):
|
|||||||
self.q = q
|
self.q = q
|
||||||
|
|
||||||
def get_sql(self, opts):
|
def get_sql(self, opts):
|
||||||
joins, where, params = self.q.get_sql(opts)
|
try:
|
||||||
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
joins, where, params = self.q.get_sql(opts)
|
||||||
|
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
||||||
|
except EmptyResultSet:
|
||||||
|
return SortedDict(), [], []
|
||||||
return joins, where2, params
|
return joins, where2, params
|
||||||
|
|
||||||
def get_where_clause(lookup_type, table_prefix, field_name, value):
|
def get_where_clause(lookup_type, table_prefix, field_name, value):
|
||||||
@ -662,7 +712,11 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
if lookup_type == 'in':
|
if lookup_type == 'in':
|
||||||
return '%s%s IN (%s)' % (table_prefix, field_name, ','.join(['%s' for v in value]))
|
in_string = ','.join(['%s' for id in value])
|
||||||
|
if in_string:
|
||||||
|
return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
|
||||||
|
else:
|
||||||
|
raise EmptyResultSet
|
||||||
elif lookup_type == 'range':
|
elif lookup_type == 'range':
|
||||||
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
|
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
|
||||||
elif lookup_type in ('year', 'month', 'day'):
|
elif lookup_type in ('year', 'month', 'day'):
|
||||||
@ -948,18 +1002,26 @@ def delete_objects(seen_objs):
|
|||||||
|
|
||||||
pk_list = [pk for pk,instance in seen_objs[cls]]
|
pk_list = [pk for pk,instance in seen_objs[cls]]
|
||||||
for related in cls._meta.get_all_related_many_to_many_objects():
|
for related in cls._meta.get_all_related_many_to_many_objects():
|
||||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
if not isinstance(related.field, GenericRelation):
|
||||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||||
(qn(related.field.m2m_db_table()),
|
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||||
qn(related.field.m2m_reverse_name()),
|
(qn(related.field.m2m_db_table()),
|
||||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
qn(related.field.m2m_reverse_name()),
|
||||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
||||||
|
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
||||||
for f in cls._meta.many_to_many:
|
for f in cls._meta.many_to_many:
|
||||||
|
if isinstance(f, GenericRelation):
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
|
||||||
|
args_extra = [ContentType.objects.get_for_model(cls).id]
|
||||||
|
else:
|
||||||
|
query_extra = ''
|
||||||
|
args_extra = []
|
||||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||||
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
|
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
|
||||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra,
|
||||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
|
||||||
for field in cls._meta.fields:
|
for field in cls._meta.fields:
|
||||||
if field.rel and field.null and field.rel.to in seen_objs:
|
if field.rel and field.null and field.rel.to in seen_objs:
|
||||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||||
|
@ -3,8 +3,8 @@ Field classes
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import gettext
|
from django.utils.translation import gettext
|
||||||
from util import ValidationError, smart_unicode
|
from util import ErrorList, ValidationError, smart_unicode
|
||||||
from widgets import TextInput, PasswordInput, CheckboxInput, Select, SelectMultiple
|
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
@ -15,8 +15,9 @@ __all__ = (
|
|||||||
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
|
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
|
||||||
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
|
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
|
||||||
'RegexField', 'EmailField', 'URLField', 'BooleanField',
|
'RegexField', 'EmailField', 'URLField', 'BooleanField',
|
||||||
'ChoiceField', 'MultipleChoiceField',
|
'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
|
||||||
'ComboField',
|
'ComboField', 'MultiValueField',
|
||||||
|
'SplitDateTimeField',
|
||||||
)
|
)
|
||||||
|
|
||||||
# These values, if given to to_python(), will trigger the self.required check.
|
# These values, if given to to_python(), will trigger the self.required check.
|
||||||
@ -29,11 +30,12 @@ except NameError:
|
|||||||
|
|
||||||
class Field(object):
|
class Field(object):
|
||||||
widget = TextInput # Default widget to use when rendering this type of Field.
|
widget = TextInput # Default widget to use when rendering this type of Field.
|
||||||
|
hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
|
||||||
|
|
||||||
# Tracks each time a Field instance is created. Used to retain order.
|
# Tracks each time a Field instance is created. Used to retain order.
|
||||||
creation_counter = 0
|
creation_counter = 0
|
||||||
|
|
||||||
def __init__(self, required=True, widget=None, label=None, initial=None):
|
def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None):
|
||||||
# required -- Boolean that specifies whether the field is required.
|
# required -- Boolean that specifies whether the field is required.
|
||||||
# True by default.
|
# True by default.
|
||||||
# widget -- A Widget class, or instance of a Widget class, that should be
|
# widget -- A Widget class, or instance of a Widget class, that should be
|
||||||
@ -45,9 +47,11 @@ class Field(object):
|
|||||||
# field name, if the Field is part of a Form.
|
# field name, if the Field is part of a Form.
|
||||||
# initial -- A value to use in this Field's initial display. This value is
|
# initial -- A value to use in this Field's initial display. This value is
|
||||||
# *not* used as a fallback if data isn't given.
|
# *not* used as a fallback if data isn't given.
|
||||||
|
# help_text -- An optional string to use as "help text" for this Field.
|
||||||
if label is not None:
|
if label is not None:
|
||||||
label = smart_unicode(label)
|
label = smart_unicode(label)
|
||||||
self.required, self.label, self.initial = required, label, initial
|
self.required, self.label, self.initial = required, label, initial
|
||||||
|
self.help_text = help_text
|
||||||
widget = widget or self.widget
|
widget = widget or self.widget
|
||||||
if isinstance(widget, type):
|
if isinstance(widget, type):
|
||||||
widget = widget()
|
widget = widget()
|
||||||
@ -83,17 +87,15 @@ class Field(object):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
class CharField(Field):
|
class CharField(Field):
|
||||||
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None, initial=None):
|
def __init__(self, max_length=None, min_length=None, *args, **kwargs):
|
||||||
self.max_length, self.min_length = max_length, min_length
|
self.max_length, self.min_length = max_length, min_length
|
||||||
Field.__init__(self, required, widget, label, initial)
|
super(CharField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"Validates max_length and min_length. Returns a Unicode object."
|
"Validates max_length and min_length. Returns a Unicode object."
|
||||||
Field.clean(self, value)
|
super(CharField, self).clean(value)
|
||||||
if value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
value = u''
|
return u''
|
||||||
if not self.required:
|
|
||||||
return value
|
|
||||||
value = smart_unicode(value)
|
value = smart_unicode(value)
|
||||||
if self.max_length is not None and len(value) > self.max_length:
|
if self.max_length is not None and len(value) > self.max_length:
|
||||||
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||||
@ -106,18 +108,18 @@ class CharField(Field):
|
|||||||
return {'maxlength': str(self.max_length)}
|
return {'maxlength': str(self.max_length)}
|
||||||
|
|
||||||
class IntegerField(Field):
|
class IntegerField(Field):
|
||||||
def __init__(self, max_value=None, min_value=None, required=True, widget=None, label=None, initial=None):
|
def __init__(self, max_value=None, min_value=None, *args, **kwargs):
|
||||||
self.max_value, self.min_value = max_value, min_value
|
self.max_value, self.min_value = max_value, min_value
|
||||||
Field.__init__(self, required, widget, label, initial)
|
super(IntegerField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that int() can be called on the input. Returns the result
|
Validates that int() can be called on the input. Returns the result
|
||||||
of int().
|
of int(). Returns None for empty values.
|
||||||
"""
|
"""
|
||||||
super(IntegerField, self).clean(value)
|
super(IntegerField, self).clean(value)
|
||||||
if not self.required and value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
return u''
|
return None
|
||||||
try:
|
try:
|
||||||
value = int(value)
|
value = int(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
@ -137,8 +139,8 @@ DEFAULT_DATE_INPUT_FORMATS = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
class DateField(Field):
|
class DateField(Field):
|
||||||
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
|
def __init__(self, input_formats=None, *args, **kwargs):
|
||||||
Field.__init__(self, required, widget, label, initial)
|
super(DateField, self).__init__(*args, **kwargs)
|
||||||
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
@ -146,7 +148,7 @@ class DateField(Field):
|
|||||||
Validates that the input can be converted to a date. Returns a Python
|
Validates that the input can be converted to a date. Returns a Python
|
||||||
datetime.date object.
|
datetime.date object.
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
super(DateField, self).clean(value)
|
||||||
if value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
return None
|
return None
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
@ -166,8 +168,8 @@ DEFAULT_TIME_INPUT_FORMATS = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
class TimeField(Field):
|
class TimeField(Field):
|
||||||
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
|
def __init__(self, input_formats=None, *args, **kwargs):
|
||||||
Field.__init__(self, required, widget, label, initial)
|
super(TimeField, self).__init__(*args, **kwargs)
|
||||||
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
|
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
@ -175,7 +177,7 @@ class TimeField(Field):
|
|||||||
Validates that the input can be converted to a time. Returns a Python
|
Validates that the input can be converted to a time. Returns a Python
|
||||||
datetime.time object.
|
datetime.time object.
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
super(TimeField, self).clean(value)
|
||||||
if value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
return None
|
return None
|
||||||
if isinstance(value, datetime.time):
|
if isinstance(value, datetime.time):
|
||||||
@ -200,8 +202,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
class DateTimeField(Field):
|
class DateTimeField(Field):
|
||||||
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
|
def __init__(self, input_formats=None, *args, **kwargs):
|
||||||
Field.__init__(self, required, widget, label, initial)
|
super(DateTimeField, self).__init__(*args, **kwargs)
|
||||||
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
@ -209,7 +211,7 @@ class DateTimeField(Field):
|
|||||||
Validates that the input can be converted to a datetime. Returns a
|
Validates that the input can be converted to a datetime. Returns a
|
||||||
Python datetime.datetime object.
|
Python datetime.datetime object.
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
super(DateTimeField, self).clean(value)
|
||||||
if value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
return None
|
return None
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
@ -224,14 +226,13 @@ class DateTimeField(Field):
|
|||||||
raise ValidationError(gettext(u'Enter a valid date/time.'))
|
raise ValidationError(gettext(u'Enter a valid date/time.'))
|
||||||
|
|
||||||
class RegexField(Field):
|
class RegexField(Field):
|
||||||
def __init__(self, regex, max_length=None, min_length=None, error_message=None,
|
def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
|
||||||
required=True, widget=None, label=None, initial=None):
|
|
||||||
"""
|
"""
|
||||||
regex can be either a string or a compiled regular expression object.
|
regex can be either a string or a compiled regular expression object.
|
||||||
error_message is an optional error message to use, if
|
error_message is an optional error message to use, if
|
||||||
'Enter a valid value' is too generic for you.
|
'Enter a valid value' is too generic for you.
|
||||||
"""
|
"""
|
||||||
Field.__init__(self, required, widget, label, initial)
|
super(RegexField, self).__init__(*args, **kwargs)
|
||||||
if isinstance(regex, basestring):
|
if isinstance(regex, basestring):
|
||||||
regex = re.compile(regex)
|
regex = re.compile(regex)
|
||||||
self.regex = regex
|
self.regex = regex
|
||||||
@ -243,10 +244,11 @@ class RegexField(Field):
|
|||||||
Validates that the input matches the regular expression. Returns a
|
Validates that the input matches the regular expression. Returns a
|
||||||
Unicode object.
|
Unicode object.
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
super(RegexField, self).clean(value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES:
|
||||||
|
value = u''
|
||||||
value = smart_unicode(value)
|
value = smart_unicode(value)
|
||||||
if not self.required and value == u'':
|
if value == u'':
|
||||||
return value
|
return value
|
||||||
if self.max_length is not None and len(value) > self.max_length:
|
if self.max_length is not None and len(value) > self.max_length:
|
||||||
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
|
||||||
@ -262,8 +264,9 @@ email_re = re.compile(
|
|||||||
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
|
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
|
||||||
|
|
||||||
class EmailField(RegexField):
|
class EmailField(RegexField):
|
||||||
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None, initial=None):
|
def __init__(self, max_length=None, min_length=None, *args, **kwargs):
|
||||||
RegexField.__init__(self, email_re, max_length, min_length, gettext(u'Enter a valid e-mail address.'), required, widget, label, initial)
|
RegexField.__init__(self, email_re, max_length, min_length,
|
||||||
|
gettext(u'Enter a valid e-mail address.'), *args, **kwargs)
|
||||||
|
|
||||||
url_re = re.compile(
|
url_re = re.compile(
|
||||||
r'^https?://' # http:// or https://
|
r'^https?://' # http:// or https://
|
||||||
@ -279,14 +282,16 @@ except ImportError:
|
|||||||
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
||||||
|
|
||||||
class URLField(RegexField):
|
class URLField(RegexField):
|
||||||
def __init__(self, max_length=None, min_length=None, required=True, verify_exists=False, widget=None, label=None,
|
def __init__(self, max_length=None, min_length=None, verify_exists=False,
|
||||||
initial=None, validator_user_agent=URL_VALIDATOR_USER_AGENT):
|
validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
|
||||||
RegexField.__init__(self, url_re, max_length, min_length, gettext(u'Enter a valid URL.'), required, widget, label, initial)
|
super(URLField, self).__init__(url_re, max_length, min_length, gettext(u'Enter a valid URL.'), *args, **kwargs)
|
||||||
self.verify_exists = verify_exists
|
self.verify_exists = verify_exists
|
||||||
self.user_agent = validator_user_agent
|
self.user_agent = validator_user_agent
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
value = RegexField.clean(self, value)
|
value = super(URLField, self).clean(value)
|
||||||
|
if value == u'':
|
||||||
|
return value
|
||||||
if self.verify_exists:
|
if self.verify_exists:
|
||||||
import urllib2
|
import urllib2
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -311,24 +316,43 @@ class BooleanField(Field):
|
|||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"Returns a Python boolean object."
|
"Returns a Python boolean object."
|
||||||
Field.clean(self, value)
|
super(BooleanField, self).clean(value)
|
||||||
return bool(value)
|
return bool(value)
|
||||||
|
|
||||||
|
class NullBooleanField(BooleanField):
|
||||||
|
"""
|
||||||
|
A field whose valid values are None, True and False. Invalid values are
|
||||||
|
cleaned to None.
|
||||||
|
"""
|
||||||
|
widget = NullBooleanSelect
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
return {True: True, False: False}.get(value, None)
|
||||||
|
|
||||||
class ChoiceField(Field):
|
class ChoiceField(Field):
|
||||||
def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None):
|
def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None, help_text=None):
|
||||||
if isinstance(widget, type):
|
super(ChoiceField, self).__init__(required, widget, label, initial, help_text)
|
||||||
widget = widget(choices=choices)
|
|
||||||
Field.__init__(self, required, widget, label, initial)
|
|
||||||
self.choices = choices
|
self.choices = choices
|
||||||
|
|
||||||
|
def _get_choices(self):
|
||||||
|
return self._choices
|
||||||
|
|
||||||
|
def _set_choices(self, value):
|
||||||
|
# Setting choices also sets the choices on the widget.
|
||||||
|
self._choices = value
|
||||||
|
self.widget.choices = value
|
||||||
|
|
||||||
|
choices = property(_get_choices, _set_choices)
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input is in self.choices.
|
Validates that the input is in self.choices.
|
||||||
"""
|
"""
|
||||||
value = Field.clean(self, value)
|
value = super(ChoiceField, self).clean(value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES:
|
||||||
|
value = u''
|
||||||
value = smart_unicode(value)
|
value = smart_unicode(value)
|
||||||
if not self.required and value == u'':
|
if value == u'':
|
||||||
return value
|
return value
|
||||||
valid_values = set([str(k) for k, v in self.choices])
|
valid_values = set([str(k) for k, v in self.choices])
|
||||||
if value not in valid_values:
|
if value not in valid_values:
|
||||||
@ -336,8 +360,10 @@ class ChoiceField(Field):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
class MultipleChoiceField(ChoiceField):
|
class MultipleChoiceField(ChoiceField):
|
||||||
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None):
|
hidden_widget = MultipleHiddenInput
|
||||||
ChoiceField.__init__(self, choices, required, widget, label, initial)
|
|
||||||
|
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None, help_text=None):
|
||||||
|
super(MultipleChoiceField, self).__init__(choices, required, widget, label, initial, help_text)
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
@ -361,8 +387,11 @@ class MultipleChoiceField(ChoiceField):
|
|||||||
return new_value
|
return new_value
|
||||||
|
|
||||||
class ComboField(Field):
|
class ComboField(Field):
|
||||||
def __init__(self, fields=(), required=True, widget=None, label=None, initial=None):
|
"""
|
||||||
Field.__init__(self, required, widget, label, initial)
|
A Field whose clean() method calls multiple Field clean() methods.
|
||||||
|
"""
|
||||||
|
def __init__(self, fields=(), *args, **kwargs):
|
||||||
|
super(ComboField, self).__init__(*args, **kwargs)
|
||||||
# Set 'required' to False on the individual fields, because the
|
# Set 'required' to False on the individual fields, because the
|
||||||
# required validation will be handled by ComboField, not by those
|
# required validation will be handled by ComboField, not by those
|
||||||
# individual fields.
|
# individual fields.
|
||||||
@ -375,7 +404,88 @@ class ComboField(Field):
|
|||||||
Validates the given value against all of self.fields, which is a
|
Validates the given value against all of self.fields, which is a
|
||||||
list of Field instances.
|
list of Field instances.
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
super(ComboField, self).clean(value)
|
||||||
for field in self.fields:
|
for field in self.fields:
|
||||||
value = field.clean(value)
|
value = field.clean(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
class MultiValueField(Field):
|
||||||
|
"""
|
||||||
|
A Field that is composed of multiple Fields.
|
||||||
|
|
||||||
|
Its clean() method takes a "decompressed" list of values. Each value in
|
||||||
|
this list is cleaned by the corresponding field -- the first value is
|
||||||
|
cleaned by the first field, the second value is cleaned by the second
|
||||||
|
field, etc. Once all fields are cleaned, the list of clean values is
|
||||||
|
"compressed" into a single value.
|
||||||
|
|
||||||
|
Subclasses should implement compress(), which specifies how a list of
|
||||||
|
valid values should be converted to a single value. Subclasses should not
|
||||||
|
have to implement clean().
|
||||||
|
|
||||||
|
You'll probably want to use this with MultiWidget.
|
||||||
|
"""
|
||||||
|
def __init__(self, fields=(), *args, **kwargs):
|
||||||
|
super(MultiValueField, self).__init__(*args, **kwargs)
|
||||||
|
# Set 'required' to False on the individual fields, because the
|
||||||
|
# required validation will be handled by MultiValueField, not by those
|
||||||
|
# individual fields.
|
||||||
|
for f in fields:
|
||||||
|
f.required = False
|
||||||
|
self.fields = fields
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
"""
|
||||||
|
Validates every value in the given list. A value is validated against
|
||||||
|
the corresponding Field in self.fields.
|
||||||
|
|
||||||
|
For example, if this MultiValueField was instantiated with
|
||||||
|
fields=(DateField(), TimeField()), clean() would call
|
||||||
|
DateField.clean(value[0]) and TimeField.clean(value[1]).
|
||||||
|
"""
|
||||||
|
clean_data = []
|
||||||
|
errors = ErrorList()
|
||||||
|
if self.required and not value:
|
||||||
|
raise ValidationError(gettext(u'This field is required.'))
|
||||||
|
elif not self.required and not value:
|
||||||
|
return self.compress([])
|
||||||
|
if not isinstance(value, (list, tuple)):
|
||||||
|
raise ValidationError(gettext(u'Enter a list of values.'))
|
||||||
|
for i, field in enumerate(self.fields):
|
||||||
|
try:
|
||||||
|
field_value = value[i]
|
||||||
|
except KeyError:
|
||||||
|
field_value = None
|
||||||
|
if self.required and field_value in EMPTY_VALUES:
|
||||||
|
raise ValidationError(gettext(u'This field is required.'))
|
||||||
|
try:
|
||||||
|
clean_data.append(field.clean(field_value))
|
||||||
|
except ValidationError, e:
|
||||||
|
# Collect all validation errors in a single list, which we'll
|
||||||
|
# raise at the end of clean(), rather than raising a single
|
||||||
|
# exception for the first error we encounter.
|
||||||
|
errors.extend(e.messages)
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
return self.compress(clean_data)
|
||||||
|
|
||||||
|
def compress(self, data_list):
|
||||||
|
"""
|
||||||
|
Returns a single value for the given list of values. The values can be
|
||||||
|
assumed to be valid.
|
||||||
|
|
||||||
|
For example, if this MultiValueField was instantiated with
|
||||||
|
fields=(DateField(), TimeField()), this might return a datetime
|
||||||
|
object created by combining the date and time in data_list.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError('Subclasses must implement this method.')
|
||||||
|
|
||||||
|
class SplitDateTimeField(MultiValueField):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
fields = (DateField(), TimeField())
|
||||||
|
super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
|
||||||
|
|
||||||
|
def compress(self, data_list):
|
||||||
|
if data_list:
|
||||||
|
return datetime.datetime.combine(*data_list)
|
||||||
|
return None
|
||||||
|
@ -5,8 +5,8 @@ Form classes
|
|||||||
from django.utils.datastructures import SortedDict, MultiValueDict
|
from django.utils.datastructures import SortedDict, MultiValueDict
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from fields import Field
|
from fields import Field
|
||||||
from widgets import TextInput, Textarea, HiddenInput
|
from widgets import TextInput, Textarea, HiddenInput, MultipleHiddenInput
|
||||||
from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError
|
from util import flatatt, StrAndUnicode, ErrorDict, ErrorList, ValidationError
|
||||||
|
|
||||||
__all__ = ('BaseForm', 'Form')
|
__all__ = ('BaseForm', 'Form')
|
||||||
|
|
||||||
@ -26,12 +26,15 @@ class SortedDictFromList(SortedDict):
|
|||||||
self.keyOrder = [d[0] for d in data]
|
self.keyOrder = [d[0] for d in data]
|
||||||
dict.__init__(self, dict(data))
|
dict.__init__(self, dict(data))
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return SortedDictFromList(self.items())
|
||||||
|
|
||||||
class DeclarativeFieldsMetaclass(type):
|
class DeclarativeFieldsMetaclass(type):
|
||||||
"Metaclass that converts Field attributes to a dictionary called 'fields'."
|
"Metaclass that converts Field attributes to a dictionary called 'base_fields'."
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)]
|
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
|
||||||
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
|
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
|
||||||
attrs['fields'] = SortedDictFromList(fields)
|
attrs['base_fields'] = SortedDictFromList(fields)
|
||||||
return type.__new__(cls, name, bases, attrs)
|
return type.__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
class BaseForm(StrAndUnicode):
|
class BaseForm(StrAndUnicode):
|
||||||
@ -39,13 +42,19 @@ class BaseForm(StrAndUnicode):
|
|||||||
# class is different than Form. See the comments by the Form class for more
|
# class is different than Form. See the comments by the Form class for more
|
||||||
# information. Any improvements to the form API should be made to *this*
|
# information. Any improvements to the form API should be made to *this*
|
||||||
# class, not to the Form class.
|
# class, not to the Form class.
|
||||||
def __init__(self, data=None, auto_id='id_%s', prefix=None):
|
def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):
|
||||||
self.ignore_errors = data is None
|
self.is_bound = data is not None
|
||||||
self.data = data or {}
|
self.data = data or {}
|
||||||
self.auto_id = auto_id
|
self.auto_id = auto_id
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
self.clean_data = None # Stores the data after clean() has been called.
|
self.initial = initial or {}
|
||||||
self.__errors = None # Stores the errors after clean() has been called.
|
self.__errors = None # Stores the errors after clean() has been called.
|
||||||
|
# The base_fields class attribute is the *class-wide* definition of
|
||||||
|
# fields. Because a particular *instance* of the class might want to
|
||||||
|
# alter self.fields, we create self.fields here by copying base_fields.
|
||||||
|
# Instances should always modify self.fields; they should not modify
|
||||||
|
# self.base_fields.
|
||||||
|
self.fields = self.base_fields.copy()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.as_table()
|
return self.as_table()
|
||||||
@ -74,7 +83,7 @@ class BaseForm(StrAndUnicode):
|
|||||||
Returns True if the form has no errors. Otherwise, False. If errors are
|
Returns True if the form has no errors. Otherwise, False. If errors are
|
||||||
being ignored, returns False.
|
being ignored, returns False.
|
||||||
"""
|
"""
|
||||||
return not self.ignore_errors and not bool(self.errors)
|
return self.is_bound and not bool(self.errors)
|
||||||
|
|
||||||
def add_prefix(self, field_name):
|
def add_prefix(self, field_name):
|
||||||
"""
|
"""
|
||||||
@ -85,7 +94,7 @@ class BaseForm(StrAndUnicode):
|
|||||||
"""
|
"""
|
||||||
return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name
|
return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name
|
||||||
|
|
||||||
def _html_output(self, normal_row, error_row, row_ender, errors_on_separate_row):
|
def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
|
||||||
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
|
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
|
||||||
top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
|
top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
|
||||||
output, hidden_fields = [], []
|
output, hidden_fields = [], []
|
||||||
@ -100,7 +109,11 @@ class BaseForm(StrAndUnicode):
|
|||||||
if errors_on_separate_row and bf_errors:
|
if errors_on_separate_row and bf_errors:
|
||||||
output.append(error_row % bf_errors)
|
output.append(error_row % bf_errors)
|
||||||
label = bf.label and bf.label_tag(escape(bf.label + ':')) or ''
|
label = bf.label and bf.label_tag(escape(bf.label + ':')) or ''
|
||||||
output.append(normal_row % {'errors': bf_errors, 'label': label, 'field': bf})
|
if field.help_text:
|
||||||
|
help_text = help_text_html % field.help_text
|
||||||
|
else:
|
||||||
|
help_text = u''
|
||||||
|
output.append(normal_row % {'errors': bf_errors, 'label': label, 'field': unicode(bf), 'help_text': help_text})
|
||||||
if top_errors:
|
if top_errors:
|
||||||
output.insert(0, error_row % top_errors)
|
output.insert(0, error_row % top_errors)
|
||||||
if hidden_fields: # Insert any hidden fields in the last row.
|
if hidden_fields: # Insert any hidden fields in the last row.
|
||||||
@ -115,15 +128,15 @@ class BaseForm(StrAndUnicode):
|
|||||||
|
|
||||||
def as_table(self):
|
def as_table(self):
|
||||||
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
|
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
|
||||||
return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', False)
|
return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
|
||||||
|
|
||||||
def as_ul(self):
|
def as_ul(self):
|
||||||
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
|
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
|
||||||
return self._html_output(u'<li>%(errors)s%(label)s %(field)s</li>', u'<li>%s</li>', '</li>', False)
|
return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
|
||||||
|
|
||||||
def as_p(self):
|
def as_p(self):
|
||||||
"Returns this form rendered as HTML <p>s."
|
"Returns this form rendered as HTML <p>s."
|
||||||
return self._html_output(u'<p>%(label)s %(field)s</p>', u'<p>%s</p>', '</p>', True)
|
return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'<p>%s</p>', '</p>', u' %s', True)
|
||||||
|
|
||||||
def non_field_errors(self):
|
def non_field_errors(self):
|
||||||
"""
|
"""
|
||||||
@ -137,11 +150,11 @@ class BaseForm(StrAndUnicode):
|
|||||||
"""
|
"""
|
||||||
Cleans all of self.data and populates self.__errors and self.clean_data.
|
Cleans all of self.data and populates self.__errors and self.clean_data.
|
||||||
"""
|
"""
|
||||||
self.clean_data = {}
|
|
||||||
errors = ErrorDict()
|
errors = ErrorDict()
|
||||||
if self.ignore_errors: # Stop further processing.
|
if not self.is_bound: # Stop further processing.
|
||||||
self.__errors = errors
|
self.__errors = errors
|
||||||
return
|
return
|
||||||
|
self.clean_data = {}
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
# value_from_datadict() gets the data from the dictionary.
|
# value_from_datadict() gets the data from the dictionary.
|
||||||
# Each widget type knows how to retrieve its own data, because some
|
# Each widget type knows how to retrieve its own data, because some
|
||||||
@ -160,7 +173,7 @@ class BaseForm(StrAndUnicode):
|
|||||||
except ValidationError, e:
|
except ValidationError, e:
|
||||||
errors[NON_FIELD_ERRORS] = e.messages
|
errors[NON_FIELD_ERRORS] = e.messages
|
||||||
if errors:
|
if errors:
|
||||||
self.clean_data = None
|
delattr(self, 'clean_data')
|
||||||
self.__errors = errors
|
self.__errors = errors
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
@ -218,8 +231,8 @@ class BoundField(StrAndUnicode):
|
|||||||
auto_id = self.auto_id
|
auto_id = self.auto_id
|
||||||
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
|
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
|
||||||
attrs['id'] = auto_id
|
attrs['id'] = auto_id
|
||||||
if self.form.ignore_errors:
|
if not self.form.is_bound:
|
||||||
data = self.field.initial
|
data = self.form.initial.get(self.name, self.field.initial)
|
||||||
else:
|
else:
|
||||||
data = self.data
|
data = self.data
|
||||||
return widget.render(self.html_name, data, attrs=attrs)
|
return widget.render(self.html_name, data, attrs=attrs)
|
||||||
@ -238,7 +251,7 @@ class BoundField(StrAndUnicode):
|
|||||||
"""
|
"""
|
||||||
Returns a string of HTML for representing this as an <input type="hidden">.
|
Returns a string of HTML for representing this as an <input type="hidden">.
|
||||||
"""
|
"""
|
||||||
return self.as_widget(HiddenInput(), attrs)
|
return self.as_widget(self.field.hidden_widget(), attrs)
|
||||||
|
|
||||||
def _data(self):
|
def _data(self):
|
||||||
"""
|
"""
|
||||||
@ -247,17 +260,20 @@ class BoundField(StrAndUnicode):
|
|||||||
return self.field.widget.value_from_datadict(self.form.data, self.html_name)
|
return self.field.widget.value_from_datadict(self.form.data, self.html_name)
|
||||||
data = property(_data)
|
data = property(_data)
|
||||||
|
|
||||||
def label_tag(self, contents=None):
|
def label_tag(self, contents=None, attrs=None):
|
||||||
"""
|
"""
|
||||||
Wraps the given contents in a <label>, if the field has an ID attribute.
|
Wraps the given contents in a <label>, if the field has an ID attribute.
|
||||||
Does not HTML-escape the contents. If contents aren't given, uses the
|
Does not HTML-escape the contents. If contents aren't given, uses the
|
||||||
field's HTML-escaped label.
|
field's HTML-escaped label.
|
||||||
|
|
||||||
|
If attrs are given, they're used as HTML attributes on the <label> tag.
|
||||||
"""
|
"""
|
||||||
contents = contents or escape(self.label)
|
contents = contents or escape(self.label)
|
||||||
widget = self.field.widget
|
widget = self.field.widget
|
||||||
id_ = widget.attrs.get('id') or self.auto_id
|
id_ = widget.attrs.get('id') or self.auto_id
|
||||||
if id_:
|
if id_:
|
||||||
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents)
|
attrs = attrs and flatatt(attrs) or ''
|
||||||
|
contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, contents)
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
def _is_hidden(self):
|
def _is_hidden(self):
|
||||||
|
@ -5,9 +5,9 @@ and database field objects.
|
|||||||
|
|
||||||
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
|
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
|
||||||
|
|
||||||
__all__ = ('form_for_model', 'form_for_instance', 'form_for_fields')
|
__all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields')
|
||||||
|
|
||||||
def create(self, save=True):
|
def model_save(self, commit=True):
|
||||||
"""
|
"""
|
||||||
Creates and returns model instance according to self.clean_data.
|
Creates and returns model instance according to self.clean_data.
|
||||||
|
|
||||||
@ -15,61 +15,84 @@ def create(self, save=True):
|
|||||||
"""
|
"""
|
||||||
if self.errors:
|
if self.errors:
|
||||||
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
|
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
|
||||||
obj = self._model(**self.clean_data)
|
return save_instance(self, self._model(), commit)
|
||||||
if save:
|
|
||||||
obj.save()
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def make_apply_changes(opts, instance):
|
def save_instance(form, instance, commit=True):
|
||||||
"Returns the apply_changes() method for a form_for_instance Form."
|
"""
|
||||||
|
Saves bound Form ``form``'s clean_data into model instance ``instance``.
|
||||||
|
|
||||||
|
Assumes ``form`` has a field for every non-AutoField database field in
|
||||||
|
``instance``. If commit=True, then the changes to ``instance`` will be
|
||||||
|
saved to the database. Returns ``instance``.
|
||||||
|
"""
|
||||||
from django.db import models
|
from django.db import models
|
||||||
def apply_changes(self, save=True):
|
opts = instance.__class__._meta
|
||||||
if self.errors:
|
if form.errors:
|
||||||
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
||||||
clean_data = self.clean_data
|
clean_data = form.clean_data
|
||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields:
|
||||||
if isinstance(f, models.AutoField):
|
if isinstance(f, models.AutoField):
|
||||||
continue
|
continue
|
||||||
|
setattr(instance, f.attname, clean_data[f.name])
|
||||||
|
if commit:
|
||||||
|
instance.save()
|
||||||
|
for f in opts.many_to_many:
|
||||||
setattr(instance, f.attname, clean_data[f.name])
|
setattr(instance, f.attname, clean_data[f.name])
|
||||||
if save:
|
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
|
||||||
instance.save()
|
# data will be lost. This happens because a many-to-many options cannot be
|
||||||
return instance
|
# set on an object until after it's saved. Maybe we should raise an
|
||||||
return apply_changes
|
# 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.
|
Returns a Form class for the given Django model class.
|
||||||
|
|
||||||
Provide 'form' if you want to use a custom BaseForm subclass.
|
Provide ``form`` if you want to use a custom BaseForm subclass.
|
||||||
|
|
||||||
|
Provide ``formfield_callback`` if you want to define different logic for
|
||||||
|
determining the formfield for a given database field. It's a callable that
|
||||||
|
takes a database Field instance and returns a form Field instance.
|
||||||
"""
|
"""
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
field_list = []
|
field_list = []
|
||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields + opts.many_to_many:
|
||||||
formfield = f.formfield()
|
formfield = formfield_callback(f)
|
||||||
if formfield:
|
if formfield:
|
||||||
field_list.append((f.name, formfield))
|
field_list.append((f.name, formfield))
|
||||||
fields = SortedDictFromList(field_list)
|
fields = SortedDictFromList(field_list)
|
||||||
return type(opts.object_name + 'Form', (form,), {'fields': fields, '_model': model, 'create': create})
|
return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save})
|
||||||
|
|
||||||
def form_for_instance(instance, form=BaseForm):
|
def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
|
||||||
"""
|
"""
|
||||||
Returns a Form class for the given Django model instance.
|
Returns a Form class for the given Django model instance.
|
||||||
|
|
||||||
Provide 'form' if you want to use a custom BaseForm subclass.
|
Provide ``form`` if you want to use a custom BaseForm subclass.
|
||||||
|
|
||||||
|
Provide ``formfield_callback`` if you want to define different logic for
|
||||||
|
determining the formfield for a given database field. It's a callable that
|
||||||
|
takes a database Field instance, plus **kwargs, and returns a form Field
|
||||||
|
instance with the given kwargs (i.e. 'initial').
|
||||||
"""
|
"""
|
||||||
model = instance.__class__
|
model = instance.__class__
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
field_list = []
|
field_list = []
|
||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields + opts.many_to_many:
|
||||||
current_value = f.value_from_object(instance)
|
current_value = f.value_from_object(instance)
|
||||||
formfield = f.formfield(initial=current_value)
|
formfield = formfield_callback(f, initial=current_value)
|
||||||
if formfield:
|
if formfield:
|
||||||
field_list.append((f.name, formfield))
|
field_list.append((f.name, formfield))
|
||||||
fields = SortedDictFromList(field_list)
|
fields = SortedDictFromList(field_list)
|
||||||
return type(opts.object_name + 'InstanceForm', (form,),
|
return type(opts.object_name + 'InstanceForm', (form,),
|
||||||
{'fields': fields, '_model': model, 'apply_changes': make_apply_changes(opts, instance)})
|
{'base_fields': fields, '_model': model, 'save': make_instance_save(instance)})
|
||||||
|
|
||||||
def form_for_fields(field_list):
|
def form_for_fields(field_list):
|
||||||
"Returns a Form class for the given list of Django database field instances."
|
"Returns a Form class for the given list of Django database field instances."
|
||||||
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
|
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
|
||||||
return type('FormForFields', (BaseForm,), {'fields': fields})
|
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.html import escape
|
||||||
|
|
||||||
|
# Converts a dictionary to a single string with key="value", XML-style with
|
||||||
|
# a leading space. Assumes keys do not need to be XML-escaped.
|
||||||
|
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
||||||
|
|
||||||
def smart_unicode(s):
|
def smart_unicode(s):
|
||||||
if not isinstance(s, basestring):
|
if not isinstance(s, basestring):
|
||||||
|
@ -3,14 +3,16 @@ HTML Widget classes
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
|
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',
|
||||||
'Textarea', 'CheckboxInput',
|
'FileInput', 'Textarea', 'CheckboxInput',
|
||||||
'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
|
'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
|
||||||
|
'MultiWidget', 'SplitDateTimeWidget',
|
||||||
)
|
)
|
||||||
|
|
||||||
from util import StrAndUnicode, smart_unicode
|
from util import flatatt, StrAndUnicode, smart_unicode
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
from django.utils.translation import gettext
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -18,10 +20,6 @@ try:
|
|||||||
except NameError:
|
except NameError:
|
||||||
from sets import Set as set # Python 2.3 fallback
|
from sets import Set as set # Python 2.3 fallback
|
||||||
|
|
||||||
# Converts a dictionary to a single string with key="value", XML-style with
|
|
||||||
# a leading space. Assumes keys do not need to be XML-escaped.
|
|
||||||
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
|
||||||
|
|
||||||
class Widget(object):
|
class Widget(object):
|
||||||
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
||||||
|
|
||||||
@ -87,6 +85,26 @@ class HiddenInput(Input):
|
|||||||
input_type = 'hidden'
|
input_type = 'hidden'
|
||||||
is_hidden = True
|
is_hidden = True
|
||||||
|
|
||||||
|
class MultipleHiddenInput(HiddenInput):
|
||||||
|
"""
|
||||||
|
A widget that handles <input type="hidden"> for fields that have a list
|
||||||
|
of values.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None, choices=()):
|
||||||
|
# choices can be any iterable
|
||||||
|
self.attrs = attrs or {}
|
||||||
|
self.choices = choices
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
|
if value is None: value = []
|
||||||
|
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
||||||
|
return u'\n'.join([(u'<input%s />' % flatatt(dict(value=smart_unicode(v), **final_attrs))) for v in value])
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, name):
|
||||||
|
if isinstance(data, MultiValueDict):
|
||||||
|
return data.getlist(name)
|
||||||
|
return data.get(name, None)
|
||||||
|
|
||||||
class FileInput(Input):
|
class FileInput(Input):
|
||||||
input_type = 'file'
|
input_type = 'file'
|
||||||
|
|
||||||
@ -118,9 +136,11 @@ class CheckboxInput(Widget):
|
|||||||
|
|
||||||
class Select(Widget):
|
class Select(Widget):
|
||||||
def __init__(self, attrs=None, choices=()):
|
def __init__(self, attrs=None, choices=()):
|
||||||
# choices can be any iterable
|
|
||||||
self.attrs = attrs or {}
|
self.attrs = attrs or {}
|
||||||
self.choices = choices
|
# choices can be any iterable, but we may need to render this widget
|
||||||
|
# multiple times. Thus, collapse it into a list so it can be consumed
|
||||||
|
# more than once.
|
||||||
|
self.choices = list(choices)
|
||||||
|
|
||||||
def render(self, name, value, attrs=None, choices=()):
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
@ -134,6 +154,25 @@ class Select(Widget):
|
|||||||
output.append(u'</select>')
|
output.append(u'</select>')
|
||||||
return u'\n'.join(output)
|
return u'\n'.join(output)
|
||||||
|
|
||||||
|
class NullBooleanSelect(Select):
|
||||||
|
"""
|
||||||
|
A Select Widget intended to be used with NullBooleanField.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
choices = ((u'1', gettext('Unknown')), (u'2', gettext('Yes')), (u'3', gettext('No')))
|
||||||
|
super(NullBooleanSelect, self).__init__(attrs, choices)
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
|
try:
|
||||||
|
value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value]
|
||||||
|
except KeyError:
|
||||||
|
value = u'1'
|
||||||
|
return super(NullBooleanSelect, self).render(name, value, attrs, choices)
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, name):
|
||||||
|
value = data.get(name, None)
|
||||||
|
return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
|
||||||
|
|
||||||
class SelectMultiple(Widget):
|
class SelectMultiple(Widget):
|
||||||
def __init__(self, attrs=None, choices=()):
|
def __init__(self, attrs=None, choices=()):
|
||||||
# choices can be any iterable
|
# choices can be any iterable
|
||||||
@ -162,14 +201,15 @@ class RadioInput(StrAndUnicode):
|
|||||||
def __init__(self, name, value, attrs, choice, index):
|
def __init__(self, name, value, attrs, choice, index):
|
||||||
self.name, self.value = name, value
|
self.name, self.value = name, value
|
||||||
self.attrs = attrs
|
self.attrs = attrs
|
||||||
self.choice_value, self.choice_label = choice
|
self.choice_value = smart_unicode(choice[0])
|
||||||
|
self.choice_label = smart_unicode(choice[1])
|
||||||
self.index = index
|
self.index = index
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
||||||
|
|
||||||
def is_checked(self):
|
def is_checked(self):
|
||||||
return self.value == smart_unicode(self.choice_value)
|
return self.value == self.choice_value
|
||||||
|
|
||||||
def tag(self):
|
def tag(self):
|
||||||
if self.attrs.has_key('id'):
|
if self.attrs.has_key('id'):
|
||||||
@ -218,11 +258,16 @@ class RadioSelect(Select):
|
|||||||
class CheckboxSelectMultiple(SelectMultiple):
|
class CheckboxSelectMultiple(SelectMultiple):
|
||||||
def render(self, name, value, attrs=None, choices=()):
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
if value is None: value = []
|
if value is None: value = []
|
||||||
|
has_id = attrs and attrs.has_key('id')
|
||||||
final_attrs = self.build_attrs(attrs, name=name)
|
final_attrs = self.build_attrs(attrs, name=name)
|
||||||
output = [u'<ul>']
|
output = [u'<ul>']
|
||||||
str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
|
str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
|
||||||
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
|
for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
|
||||||
for option_value, option_label in 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)
|
option_value = smart_unicode(option_value)
|
||||||
rendered_cb = cb.render(name, option_value)
|
rendered_cb = cb.render(name, option_value)
|
||||||
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label))))
|
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label))))
|
||||||
@ -235,3 +280,66 @@ class CheckboxSelectMultiple(SelectMultiple):
|
|||||||
id_ += '_0'
|
id_ += '_0'
|
||||||
return id_
|
return id_
|
||||||
id_for_label = classmethod(id_for_label)
|
id_for_label = classmethod(id_for_label)
|
||||||
|
|
||||||
|
class MultiWidget(Widget):
|
||||||
|
"""
|
||||||
|
A widget that is composed of multiple widgets.
|
||||||
|
|
||||||
|
Its render() method takes a "decompressed" list of values, not a single
|
||||||
|
value. Each value in this list is rendered in the corresponding widget --
|
||||||
|
the first value is rendered in the first widget, the second value is
|
||||||
|
rendered in the second widget, etc.
|
||||||
|
|
||||||
|
Subclasses should implement decompress(), which specifies how a single
|
||||||
|
value should be converted to a list of values. Subclasses should not
|
||||||
|
have to implement clean().
|
||||||
|
|
||||||
|
Subclasses may implement format_output(), which takes the list of rendered
|
||||||
|
widgets and returns HTML that formats them any way you'd like.
|
||||||
|
|
||||||
|
You'll probably want to use this with MultiValueField.
|
||||||
|
"""
|
||||||
|
def __init__(self, widgets, attrs=None):
|
||||||
|
self.widgets = [isinstance(w, type) and w() or w for w in widgets]
|
||||||
|
super(MultiWidget, self).__init__(attrs)
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None):
|
||||||
|
# value is a list of values, each corresponding to a widget
|
||||||
|
# in self.widgets.
|
||||||
|
if not isinstance(value, list):
|
||||||
|
value = self.decompress(value)
|
||||||
|
output = []
|
||||||
|
for i, widget in enumerate(self.widgets):
|
||||||
|
try:
|
||||||
|
widget_value = value[i]
|
||||||
|
except KeyError:
|
||||||
|
widget_value = None
|
||||||
|
output.append(widget.render(name + '_%s' % i, widget_value, attrs))
|
||||||
|
return self.format_output(output)
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, name):
|
||||||
|
return [data.get(name + '_%s' % i) for i in range(len(self.widgets))]
|
||||||
|
|
||||||
|
def format_output(self, rendered_widgets):
|
||||||
|
return u''.join(rendered_widgets)
|
||||||
|
|
||||||
|
def decompress(self, value):
|
||||||
|
"""
|
||||||
|
Returns a list of decompressed values for the given compressed value.
|
||||||
|
The given value can be assumed to be valid, but not necessarily
|
||||||
|
non-empty.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError('Subclasses must implement this method.')
|
||||||
|
|
||||||
|
class SplitDateTimeWidget(MultiWidget):
|
||||||
|
"""
|
||||||
|
A Widget that splits datetime input into two <input type="text"> boxes.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs))
|
||||||
|
super(SplitDateTimeWidget, self).__init__(widgets, attrs)
|
||||||
|
|
||||||
|
def decompress(self, value):
|
||||||
|
if value:
|
||||||
|
return [value.date(), value.time()]
|
||||||
|
return [None, None]
|
||||||
|
@ -569,7 +569,7 @@ class NullBooleanField(SelectField):
|
|||||||
"This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None"
|
"This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None"
|
||||||
def __init__(self, field_name, is_required=False, validator_list=None):
|
def __init__(self, field_name, is_required=False, validator_list=None):
|
||||||
if validator_list is None: validator_list = []
|
if validator_list is None: validator_list = []
|
||||||
SelectField.__init__(self, field_name, choices=[('1', 'Unknown'), ('2', 'Yes'), ('3', 'No')],
|
SelectField.__init__(self, field_name, choices=[('1', _('Unknown')), ('2', _('Yes')), ('3', _('No'))],
|
||||||
is_required=is_required, validator_list=validator_list)
|
is_required=is_required, validator_list=validator_list)
|
||||||
|
|
||||||
def render(self, data):
|
def render(self, data):
|
||||||
@ -958,7 +958,9 @@ class USStateField(TextField):
|
|||||||
raise validators.CriticalValidationError, e.messages
|
raise validators.CriticalValidationError, e.messages
|
||||||
|
|
||||||
def html2python(data):
|
def html2python(data):
|
||||||
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)
|
html2python = staticmethod(html2python)
|
||||||
|
|
||||||
class CommaSeparatedIntegerField(TextField):
|
class CommaSeparatedIntegerField(TextField):
|
||||||
|
@ -70,7 +70,7 @@ class SortedDict(dict):
|
|||||||
return self.keyOrder[:]
|
return self.keyOrder[:]
|
||||||
|
|
||||||
def values(self):
|
def values(self):
|
||||||
return [dict.__getitem__(self,k) for k in self.keyOrder]
|
return [dict.__getitem__(self, k) for k in self.keyOrder]
|
||||||
|
|
||||||
def update(self, dict):
|
def update(self, dict):
|
||||||
for k, v in dict.items():
|
for k, v in dict.items():
|
||||||
@ -81,6 +81,10 @@ class SortedDict(dict):
|
|||||||
self.keyOrder.append(key)
|
self.keyOrder.append(key)
|
||||||
return dict.setdefault(self, key, default)
|
return dict.setdefault(self, key, default)
|
||||||
|
|
||||||
|
def value_for_index(self, index):
|
||||||
|
"Returns the value of the item at the given zero-based index."
|
||||||
|
return self[self.keyOrder[index]]
|
||||||
|
|
||||||
class MultiValueDictKeyError(KeyError):
|
class MultiValueDictKeyError(KeyError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
simplejson 1.3
|
simplejson 1.5
|
||||||
Copyright (c) 2006 Bob Ippolito
|
Copyright (c) 2006 Bob Ippolito
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
@ -27,6 +27,21 @@ Encoding basic Python object hierarchies::
|
|||||||
>>> io.getvalue()
|
>>> io.getvalue()
|
||||||
'["streaming API"]'
|
'["streaming API"]'
|
||||||
|
|
||||||
|
Compact encoding::
|
||||||
|
|
||||||
|
>>> import simplejson
|
||||||
|
>>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
|
||||||
|
'[1,2,3,{"4":5,"6":7}]'
|
||||||
|
|
||||||
|
Pretty printing::
|
||||||
|
|
||||||
|
>>> import simplejson
|
||||||
|
>>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
|
||||||
|
{
|
||||||
|
"4": 5,
|
||||||
|
"6": 7
|
||||||
|
}
|
||||||
|
|
||||||
Decoding JSON::
|
Decoding JSON::
|
||||||
|
|
||||||
>>> import simplejson
|
>>> import simplejson
|
||||||
@ -68,10 +83,10 @@ Extending JSONEncoder::
|
|||||||
['[', '2.0', ', ', '1.0', ']']
|
['[', '2.0', ', ', '1.0', ']']
|
||||||
|
|
||||||
|
|
||||||
Note that the JSON produced by this module is a subset of YAML,
|
Note that the JSON produced by this module's default settings
|
||||||
so it may be used as a serializer for that as well.
|
is a subset of YAML, so it may be used as a serializer for that as well.
|
||||||
"""
|
"""
|
||||||
__version__ = '1.3'
|
__version__ = '1.5'
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'dump', 'dumps', 'load', 'loads',
|
'dump', 'dumps', 'load', 'loads',
|
||||||
'JSONDecoder', 'JSONEncoder',
|
'JSONDecoder', 'JSONEncoder',
|
||||||
@ -81,7 +96,7 @@ from django.utils.simplejson.decoder import JSONDecoder
|
|||||||
from django.utils.simplejson.encoder import JSONEncoder
|
from django.utils.simplejson.encoder import JSONEncoder
|
||||||
|
|
||||||
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||||
allow_nan=True, cls=None, **kw):
|
allow_nan=True, cls=None, indent=None, **kw):
|
||||||
"""
|
"""
|
||||||
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
|
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
|
||||||
``.write()``-supporting file-like object).
|
``.write()``-supporting file-like object).
|
||||||
@ -105,6 +120,10 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
|||||||
in strict compliance of the JSON specification, instead of using the
|
in strict compliance of the JSON specification, instead of using the
|
||||||
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
||||||
|
|
||||||
|
If ``indent`` is a non-negative integer, then JSON array elements and object
|
||||||
|
members will be pretty-printed with that indent level. An indent level
|
||||||
|
of 0 will only insert newlines. ``None`` is the most compact representation.
|
||||||
|
|
||||||
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
||||||
``.default()`` method to serialize additional types), specify it with
|
``.default()`` method to serialize additional types), specify it with
|
||||||
the ``cls`` kwarg.
|
the ``cls`` kwarg.
|
||||||
@ -112,7 +131,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
|||||||
if cls is None:
|
if cls is None:
|
||||||
cls = JSONEncoder
|
cls = JSONEncoder
|
||||||
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||||
check_circular=check_circular, allow_nan=allow_nan,
|
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
|
||||||
**kw).iterencode(obj)
|
**kw).iterencode(obj)
|
||||||
# could accelerate with writelines in some versions of Python, at
|
# could accelerate with writelines in some versions of Python, at
|
||||||
# a debuggability cost
|
# a debuggability cost
|
||||||
@ -120,7 +139,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
|||||||
fp.write(chunk)
|
fp.write(chunk)
|
||||||
|
|
||||||
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
||||||
allow_nan=True, cls=None, **kw):
|
allow_nan=True, cls=None, indent=None, separators=None, **kw):
|
||||||
"""
|
"""
|
||||||
Serialize ``obj`` to a JSON formatted ``str``.
|
Serialize ``obj`` to a JSON formatted ``str``.
|
||||||
|
|
||||||
@ -141,14 +160,26 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
|||||||
strict compliance of the JSON specification, instead of using the
|
strict compliance of the JSON specification, instead of using the
|
||||||
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
|
||||||
|
|
||||||
|
If ``indent`` is a non-negative integer, then JSON array elements and
|
||||||
|
object members will be pretty-printed with that indent level. An indent
|
||||||
|
level of 0 will only insert newlines. ``None`` is the most compact
|
||||||
|
representation.
|
||||||
|
|
||||||
|
If ``separators`` is an ``(item_separator, dict_separator)`` tuple
|
||||||
|
then it will be used instead of the default ``(', ', ': ')`` separators.
|
||||||
|
``(',', ':')`` is the most compact JSON representation.
|
||||||
|
|
||||||
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
||||||
``.default()`` method to serialize additional types), specify it with
|
``.default()`` method to serialize additional types), specify it with
|
||||||
the ``cls`` kwarg.
|
the ``cls`` kwarg.
|
||||||
"""
|
"""
|
||||||
if cls is None:
|
if cls is None:
|
||||||
cls = JSONEncoder
|
cls = JSONEncoder
|
||||||
return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
return cls(
|
||||||
check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj)
|
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
||||||
|
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
|
||||||
|
separators=separators,
|
||||||
|
**kw).encode(obj)
|
||||||
|
|
||||||
def load(fp, encoding=None, cls=None, object_hook=None, **kw):
|
def load(fp, encoding=None, cls=None, object_hook=None, **kw):
|
||||||
"""
|
"""
|
||||||
|
@ -127,6 +127,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
|
|||||||
raise ValueError(errmsg("Expecting property name", s, end))
|
raise ValueError(errmsg("Expecting property name", s, end))
|
||||||
end += 1
|
end += 1
|
||||||
encoding = getattr(context, 'encoding', None)
|
encoding = getattr(context, 'encoding', None)
|
||||||
|
iterscan = JSONScanner.iterscan
|
||||||
while True:
|
while True:
|
||||||
key, end = scanstring(s, end, encoding)
|
key, end = scanstring(s, end, encoding)
|
||||||
end = _w(s, end).end()
|
end = _w(s, end).end()
|
||||||
@ -134,7 +135,7 @@ def JSONObject(match, context, _w=WHITESPACE.match):
|
|||||||
raise ValueError(errmsg("Expecting : delimiter", s, end))
|
raise ValueError(errmsg("Expecting : delimiter", s, end))
|
||||||
end = _w(s, end + 1).end()
|
end = _w(s, end + 1).end()
|
||||||
try:
|
try:
|
||||||
value, end = JSONScanner.iterscan(s, idx=end).next()
|
value, end = iterscan(s, idx=end, context=context).next()
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise ValueError(errmsg("Expecting object", s, end))
|
raise ValueError(errmsg("Expecting object", s, end))
|
||||||
pairs[key] = value
|
pairs[key] = value
|
||||||
@ -164,9 +165,10 @@ def JSONArray(match, context, _w=WHITESPACE.match):
|
|||||||
nextchar = s[end:end + 1]
|
nextchar = s[end:end + 1]
|
||||||
if nextchar == ']':
|
if nextchar == ']':
|
||||||
return values, end + 1
|
return values, end + 1
|
||||||
|
iterscan = JSONScanner.iterscan
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
value, end = JSONScanner.iterscan(s, idx=end).next()
|
value, end = iterscan(s, idx=end, context=context).next()
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise ValueError(errmsg("Expecting object", s, end))
|
raise ValueError(errmsg("Expecting object", s, end))
|
||||||
values.append(value)
|
values.append(value)
|
||||||
|
@ -3,11 +3,11 @@ Implementation of JSONEncoder
|
|||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# this should match any kind of infinity
|
|
||||||
INFCHARS = re.compile(r'[infINF]')
|
|
||||||
ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
|
ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
|
||||||
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
|
ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
|
||||||
ESCAPE_DCT = {
|
ESCAPE_DCT = {
|
||||||
|
# escape all forward slashes to prevent </script> attack
|
||||||
|
'/': '\\/',
|
||||||
'\\': '\\\\',
|
'\\': '\\\\',
|
||||||
'"': '\\"',
|
'"': '\\"',
|
||||||
'\b': '\\b',
|
'\b': '\\b',
|
||||||
@ -16,31 +16,31 @@ ESCAPE_DCT = {
|
|||||||
'\r': '\\r',
|
'\r': '\\r',
|
||||||
'\t': '\\t',
|
'\t': '\\t',
|
||||||
}
|
}
|
||||||
for i in range(20):
|
for i in range(0x20):
|
||||||
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
|
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
|
||||||
|
|
||||||
|
# assume this produces an infinity on all machines (probably not guaranteed)
|
||||||
|
INFINITY = float('1e66666')
|
||||||
|
|
||||||
def floatstr(o, allow_nan=True):
|
def floatstr(o, allow_nan=True):
|
||||||
s = str(o)
|
# Check for specials. Note that this type of test is processor- and/or
|
||||||
# If the first non-sign is a digit then it's not a special value
|
# platform-specific, so do tests which don't depend on the internals.
|
||||||
if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
|
|
||||||
return s
|
if o != o:
|
||||||
elif not allow_nan:
|
text = 'NaN'
|
||||||
|
elif o == INFINITY:
|
||||||
|
text = 'Infinity'
|
||||||
|
elif o == -INFINITY:
|
||||||
|
text = '-Infinity'
|
||||||
|
else:
|
||||||
|
return str(o)
|
||||||
|
|
||||||
|
if not allow_nan:
|
||||||
raise ValueError("Out of range float values are not JSON compliant: %r"
|
raise ValueError("Out of range float values are not JSON compliant: %r"
|
||||||
% (o,))
|
% (o,))
|
||||||
# These are the string representations on the platforms I've tried
|
|
||||||
if s == 'nan':
|
return text
|
||||||
return 'NaN'
|
|
||||||
if s == 'inf':
|
|
||||||
return 'Infinity'
|
|
||||||
if s == '-inf':
|
|
||||||
return '-Infinity'
|
|
||||||
# NaN should either be inequal to itself, or equal to everything
|
|
||||||
if o != o or o == 0.0:
|
|
||||||
return 'NaN'
|
|
||||||
# Last ditch effort, assume inf
|
|
||||||
if o < 0:
|
|
||||||
return '-Infinity'
|
|
||||||
return 'Infinity'
|
|
||||||
|
|
||||||
def encode_basestring(s):
|
def encode_basestring(s):
|
||||||
"""
|
"""
|
||||||
@ -90,8 +90,11 @@ class JSONEncoder(object):
|
|||||||
implementation (to raise ``TypeError``).
|
implementation (to raise ``TypeError``).
|
||||||
"""
|
"""
|
||||||
__all__ = ['__init__', 'default', 'encode', 'iterencode']
|
__all__ = ['__init__', 'default', 'encode', 'iterencode']
|
||||||
|
item_separator = ', '
|
||||||
|
key_separator = ': '
|
||||||
def __init__(self, skipkeys=False, ensure_ascii=True,
|
def __init__(self, skipkeys=False, ensure_ascii=True,
|
||||||
check_circular=True, allow_nan=True, sort_keys=False):
|
check_circular=True, allow_nan=True, sort_keys=False,
|
||||||
|
indent=None, separators=None):
|
||||||
"""
|
"""
|
||||||
Constructor for JSONEncoder, with sensible defaults.
|
Constructor for JSONEncoder, with sensible defaults.
|
||||||
|
|
||||||
@ -116,6 +119,15 @@ class JSONEncoder(object):
|
|||||||
If sort_keys is True, then the output of dictionaries will be
|
If sort_keys is True, then the output of dictionaries will be
|
||||||
sorted by key; this is useful for regression tests to ensure
|
sorted by key; this is useful for regression tests to ensure
|
||||||
that JSON serializations can be compared on a day-to-day basis.
|
that JSON serializations can be compared on a day-to-day basis.
|
||||||
|
|
||||||
|
If indent is a non-negative integer, then JSON array
|
||||||
|
elements and object members will be pretty-printed with that
|
||||||
|
indent level. An indent level of 0 will only insert newlines.
|
||||||
|
None is the most compact representation.
|
||||||
|
|
||||||
|
If specified, separators should be a (item_separator, key_separator)
|
||||||
|
tuple. The default is (', ', ': '). To get the most compact JSON
|
||||||
|
representation you should specify (',', ':') to eliminate whitespace.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.skipkeys = skipkeys
|
self.skipkeys = skipkeys
|
||||||
@ -123,6 +135,13 @@ class JSONEncoder(object):
|
|||||||
self.check_circular = check_circular
|
self.check_circular = check_circular
|
||||||
self.allow_nan = allow_nan
|
self.allow_nan = allow_nan
|
||||||
self.sort_keys = sort_keys
|
self.sort_keys = sort_keys
|
||||||
|
self.indent = indent
|
||||||
|
self.current_indent_level = 0
|
||||||
|
if separators is not None:
|
||||||
|
self.item_separator, self.key_separator = separators
|
||||||
|
|
||||||
|
def _newline_indent(self):
|
||||||
|
return '\n' + (' ' * (self.indent * self.current_indent_level))
|
||||||
|
|
||||||
def _iterencode_list(self, lst, markers=None):
|
def _iterencode_list(self, lst, markers=None):
|
||||||
if not lst:
|
if not lst:
|
||||||
@ -134,14 +153,25 @@ class JSONEncoder(object):
|
|||||||
raise ValueError("Circular reference detected")
|
raise ValueError("Circular reference detected")
|
||||||
markers[markerid] = lst
|
markers[markerid] = lst
|
||||||
yield '['
|
yield '['
|
||||||
|
if self.indent is not None:
|
||||||
|
self.current_indent_level += 1
|
||||||
|
newline_indent = self._newline_indent()
|
||||||
|
separator = self.item_separator + newline_indent
|
||||||
|
yield newline_indent
|
||||||
|
else:
|
||||||
|
newline_indent = None
|
||||||
|
separator = self.item_separator
|
||||||
first = True
|
first = True
|
||||||
for value in lst:
|
for value in lst:
|
||||||
if first:
|
if first:
|
||||||
first = False
|
first = False
|
||||||
else:
|
else:
|
||||||
yield ', '
|
yield separator
|
||||||
for chunk in self._iterencode(value, markers):
|
for chunk in self._iterencode(value, markers):
|
||||||
yield chunk
|
yield chunk
|
||||||
|
if newline_indent is not None:
|
||||||
|
self.current_indent_level -= 1
|
||||||
|
yield self._newline_indent()
|
||||||
yield ']'
|
yield ']'
|
||||||
if markers is not None:
|
if markers is not None:
|
||||||
del markers[markerid]
|
del markers[markerid]
|
||||||
@ -156,6 +186,15 @@ class JSONEncoder(object):
|
|||||||
raise ValueError("Circular reference detected")
|
raise ValueError("Circular reference detected")
|
||||||
markers[markerid] = dct
|
markers[markerid] = dct
|
||||||
yield '{'
|
yield '{'
|
||||||
|
key_separator = self.key_separator
|
||||||
|
if self.indent is not None:
|
||||||
|
self.current_indent_level += 1
|
||||||
|
newline_indent = self._newline_indent()
|
||||||
|
item_separator = self.item_separator + newline_indent
|
||||||
|
yield newline_indent
|
||||||
|
else:
|
||||||
|
newline_indent = None
|
||||||
|
item_separator = self.item_separator
|
||||||
first = True
|
first = True
|
||||||
if self.ensure_ascii:
|
if self.ensure_ascii:
|
||||||
encoder = encode_basestring_ascii
|
encoder = encode_basestring_ascii
|
||||||
@ -165,7 +204,7 @@ class JSONEncoder(object):
|
|||||||
if self.sort_keys:
|
if self.sort_keys:
|
||||||
keys = dct.keys()
|
keys = dct.keys()
|
||||||
keys.sort()
|
keys.sort()
|
||||||
items = [(k,dct[k]) for k in keys]
|
items = [(k, dct[k]) for k in keys]
|
||||||
else:
|
else:
|
||||||
items = dct.iteritems()
|
items = dct.iteritems()
|
||||||
for key, value in items:
|
for key, value in items:
|
||||||
@ -190,11 +229,14 @@ class JSONEncoder(object):
|
|||||||
if first:
|
if first:
|
||||||
first = False
|
first = False
|
||||||
else:
|
else:
|
||||||
yield ', '
|
yield item_separator
|
||||||
yield encoder(key)
|
yield encoder(key)
|
||||||
yield ': '
|
yield key_separator
|
||||||
for chunk in self._iterencode(value, markers):
|
for chunk in self._iterencode(value, markers):
|
||||||
yield chunk
|
yield chunk
|
||||||
|
if newline_indent is not None:
|
||||||
|
self.current_indent_level -= 1
|
||||||
|
yield self._newline_indent()
|
||||||
yield '}'
|
yield '}'
|
||||||
if markers is not None:
|
if markers is not None:
|
||||||
del markers[markerid]
|
del markers[markerid]
|
||||||
|
40
django/utils/simplejson/jsonfilter.py
Normal file
40
django/utils/simplejson/jsonfilter.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from django.utils import simplejson
|
||||||
|
import cgi
|
||||||
|
|
||||||
|
class JSONFilter(object):
|
||||||
|
def __init__(self, app, mime_type='text/x-json'):
|
||||||
|
self.app = app
|
||||||
|
self.mime_type = mime_type
|
||||||
|
|
||||||
|
def __call__(self, environ, start_response):
|
||||||
|
# Read JSON POST input to jsonfilter.json if matching mime type
|
||||||
|
response = {'status': '200 OK', 'headers': []}
|
||||||
|
def json_start_response(status, headers):
|
||||||
|
response['status'] = status
|
||||||
|
response['headers'].extend(headers)
|
||||||
|
environ['jsonfilter.mime_type'] = self.mime_type
|
||||||
|
if environ.get('REQUEST_METHOD', '') == 'POST':
|
||||||
|
if environ.get('CONTENT_TYPE', '') == self.mime_type:
|
||||||
|
args = [_ for _ in [environ.get('CONTENT_LENGTH')] if _]
|
||||||
|
data = environ['wsgi.input'].read(*map(int, args))
|
||||||
|
environ['jsonfilter.json'] = simplejson.loads(data)
|
||||||
|
res = simplejson.dumps(self.app(environ, json_start_response))
|
||||||
|
jsonp = cgi.parse_qs(environ.get('QUERY_STRING', '')).get('jsonp')
|
||||||
|
if jsonp:
|
||||||
|
content_type = 'text/javascript'
|
||||||
|
res = ''.join(jsonp + ['(', res, ')'])
|
||||||
|
elif 'Opera' in environ.get('HTTP_USER_AGENT', ''):
|
||||||
|
# Opera has bunk XMLHttpRequest support for most mime types
|
||||||
|
content_type = 'text/plain'
|
||||||
|
else:
|
||||||
|
content_type = self.mime_type
|
||||||
|
headers = [
|
||||||
|
('Content-type', content_type),
|
||||||
|
('Content-length', len(res)),
|
||||||
|
]
|
||||||
|
headers.extend(response['headers'])
|
||||||
|
start_response(response['status'], headers)
|
||||||
|
return [res]
|
||||||
|
|
||||||
|
def factory(app, global_conf, **kw):
|
||||||
|
return JSONFilter(app, **kw)
|
@ -3,11 +3,12 @@ Iterator based sre token scanner
|
|||||||
"""
|
"""
|
||||||
import sre_parse, sre_compile, sre_constants
|
import sre_parse, sre_compile, sre_constants
|
||||||
from sre_constants import BRANCH, SUBPATTERN
|
from sre_constants import BRANCH, SUBPATTERN
|
||||||
|
from re import VERBOSE, MULTILINE, DOTALL
|
||||||
import re
|
import re
|
||||||
|
|
||||||
__all__ = ['Scanner', 'pattern']
|
__all__ = ['Scanner', 'pattern']
|
||||||
|
|
||||||
FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL)
|
FLAGS = (VERBOSE | MULTILINE | DOTALL)
|
||||||
class Scanner(object):
|
class Scanner(object):
|
||||||
def __init__(self, lexicon, flags=FLAGS):
|
def __init__(self, lexicon, flags=FLAGS):
|
||||||
self.actions = [None]
|
self.actions = [None]
|
||||||
|
@ -17,7 +17,7 @@ admin
|
|||||||
The automatic Django administrative interface. For more information, see
|
The automatic Django administrative interface. For more information, see
|
||||||
`Tutorial 2`_.
|
`Tutorial 2`_.
|
||||||
|
|
||||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
.. _Tutorial 2: ../tutorial2/
|
||||||
|
|
||||||
auth
|
auth
|
||||||
====
|
====
|
||||||
@ -26,7 +26,7 @@ Django's authentication framework.
|
|||||||
|
|
||||||
See the `authentication documentation`_.
|
See the `authentication documentation`_.
|
||||||
|
|
||||||
.. _authentication documentation: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication documentation: ../authentication/
|
||||||
|
|
||||||
comments
|
comments
|
||||||
========
|
========
|
||||||
@ -46,7 +46,7 @@ A middleware for preventing Cross Site Request Forgeries
|
|||||||
|
|
||||||
See the `csrf documentation`_.
|
See the `csrf documentation`_.
|
||||||
|
|
||||||
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/
|
.. _csrf documentation: ../csrf/
|
||||||
|
|
||||||
formtools
|
formtools
|
||||||
=========
|
=========
|
||||||
@ -137,7 +137,7 @@ A framework for managing simple "flat" HTML content in a database.
|
|||||||
|
|
||||||
See the `flatpages documentation`_.
|
See the `flatpages documentation`_.
|
||||||
|
|
||||||
.. _flatpages documentation: http://www.djangoproject.com/documentation/flatpages/
|
.. _flatpages documentation: ../flatpages/
|
||||||
|
|
||||||
markup
|
markup
|
||||||
======
|
======
|
||||||
@ -157,7 +157,7 @@ A framework for managing redirects.
|
|||||||
|
|
||||||
See the `redirects documentation`_.
|
See the `redirects documentation`_.
|
||||||
|
|
||||||
.. _redirects documentation: http://www.djangoproject.com/documentation/redirects/
|
.. _redirects documentation: ../redirects/
|
||||||
|
|
||||||
sites
|
sites
|
||||||
=====
|
=====
|
||||||
@ -168,7 +168,7 @@ one or more sites.
|
|||||||
|
|
||||||
See the `sites documentation`_.
|
See the `sites documentation`_.
|
||||||
|
|
||||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
.. _sites documentation: ../sites/
|
||||||
|
|
||||||
sitemaps
|
sitemaps
|
||||||
========
|
========
|
||||||
@ -177,7 +177,7 @@ A framework for generating Google sitemap XML files.
|
|||||||
|
|
||||||
See the `sitemaps documentation`_.
|
See the `sitemaps documentation`_.
|
||||||
|
|
||||||
.. _sitemaps documentation: http://www.djangoproject.com/documentation/sitemaps/
|
.. _sitemaps documentation: ../sitemaps/
|
||||||
|
|
||||||
syndication
|
syndication
|
||||||
===========
|
===========
|
||||||
@ -186,7 +186,7 @@ A framework for generating syndication feeds, in RSS and Atom, quite easily.
|
|||||||
|
|
||||||
See the `syndication documentation`_.
|
See the `syndication documentation`_.
|
||||||
|
|
||||||
.. _syndication documentation: http://www.djangoproject.com/documentation/syndication/
|
.. _syndication documentation: ../syndication/
|
||||||
|
|
||||||
Other add-ons
|
Other add-ons
|
||||||
=============
|
=============
|
||||||
|
@ -65,7 +65,7 @@ are equivalent::
|
|||||||
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
|
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
|
||||||
PythonOption DJANGO_SETTINGS_MODULE mysite.settings
|
PythonOption DJANGO_SETTINGS_MODULE mysite.settings
|
||||||
|
|
||||||
.. _authentication system: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication system: ../authentication/
|
||||||
.. _Subversion: http://subversion.tigris.org/
|
.. _Subversion: http://subversion.tigris.org/
|
||||||
.. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html
|
.. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html
|
||||||
.. _custom permissions: http://www.djangoproject.com/documentation/authentication/#custom-permissions
|
.. _custom permissions: ../authentication/#custom-permissions
|
||||||
|
@ -98,26 +98,26 @@ change:
|
|||||||
rewrite before Django 1.0. Even if the change isn't quite that drastic,
|
rewrite before Django 1.0. Even if the change isn't quite that drastic,
|
||||||
there will at least be moderate changes.
|
there will at least be moderate changes.
|
||||||
|
|
||||||
.. _caching: http://www.djangoproject.com/documentation/cache/
|
.. _caching: ../cache/
|
||||||
.. _custom template tags and libraries: http://www.djangoproject.com/documentation/templates_python/
|
.. _custom template tags and libraries: ../templates_python/
|
||||||
.. _database lookup: http://www.djangoproject.com/documentation/db_api/
|
.. _database lookup: ../db_api/
|
||||||
.. _django-admin utility: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin utility: ../django_admin/
|
||||||
.. _fastcgi integration: http://www.djangoproject.com/documentation/fastcgi/
|
.. _fastcgi integration: ../fastcgi/
|
||||||
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
|
.. _flatpages: ../flatpages/
|
||||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
.. _generic views: ../generic_views/
|
||||||
.. _internationalization: http://www.djangoproject.com/documentation/i18n/
|
.. _internationalization: ../i18n/
|
||||||
.. _legacy database integration: http://www.djangoproject.com/documentation/legacy_databases/
|
.. _legacy database integration: ../legacy_databases/
|
||||||
.. _model definition: http://www.djangoproject.com/documentation/model_api/
|
.. _model definition: ../model_api/
|
||||||
.. _mod_python integration: http://www.djangoproject.com/documentation/modpython/
|
.. _mod_python integration: ../modpython/
|
||||||
.. _redirects: http://www.djangoproject.com/documentation/redirects/
|
.. _redirects: ../redirects/
|
||||||
.. _request/response objects: http://www.djangoproject.com/documentation/request_response/
|
.. _request/response objects: ../request_response/
|
||||||
.. _sending email: http://www.djangoproject.com/documentation/email/
|
.. _sending email: ../email/
|
||||||
.. _sessions: http://www.djangoproject.com/documentation/sessions/
|
.. _sessions: ../sessions/
|
||||||
.. _settings: http://www.djangoproject.com/documentation/settings/
|
.. _settings: ../settings/
|
||||||
.. _syndication: http://www.djangoproject.com/documentation/syndication/
|
.. _syndication: ../syndication/
|
||||||
.. _template language: http://www.djangoproject.com/documentation/templates/
|
.. _template language: ../templates/
|
||||||
.. _transactions: http://www.djangoproject.com/documentation/transactions/
|
.. _transactions: ../transactions/
|
||||||
.. _url dispatch: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _url dispatch: ../url_dispatch/
|
||||||
.. _forms and validation: http://www.djangoproject.com/documentation/forms/
|
.. _forms and validation: ../forms/
|
||||||
.. _serialization: http://www.djangoproject.com/documentation/serialization/
|
.. _serialization: ../serialization/
|
||||||
.. _authentication: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication: ../authentication/
|
||||||
|
@ -144,8 +144,8 @@ custom methods:
|
|||||||
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
|
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
|
||||||
doesn't allow profiles.
|
doesn't allow profiles.
|
||||||
|
|
||||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
.. _Django model: ../model_api/
|
||||||
.. _DEFAULT_FROM_EMAIL: http://www.djangoproject.com/documentation/settings/#default-from-email
|
.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
|
||||||
|
|
||||||
Manager functions
|
Manager functions
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
@ -271,8 +271,8 @@ previous section). You can tell them apart with ``is_authenticated()``, like so:
|
|||||||
else:
|
else:
|
||||||
# Do something for anonymous users.
|
# Do something for anonymous users.
|
||||||
|
|
||||||
.. _request objects: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
.. _request objects: ../request_response/#httprequest-objects
|
||||||
.. _session documentation: http://www.djangoproject.com/documentation/sessions/
|
.. _session documentation: ../sessions/
|
||||||
|
|
||||||
How to log a user in
|
How to log a user in
|
||||||
--------------------
|
--------------------
|
||||||
@ -441,8 +441,8 @@ block::
|
|||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
.. _forms documentation: http://www.djangoproject.com/documentation/forms/
|
.. _forms documentation: ../forms/
|
||||||
.. _site framework docs: http://www.djangoproject.com/documentation/sites/
|
.. _site framework docs: ../sites/
|
||||||
|
|
||||||
Limiting access to logged-in users that pass a test
|
Limiting access to logged-in users that pass a test
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
@ -544,7 +544,7 @@ For example::
|
|||||||
def limited_object_detail(*args, **kwargs):
|
def limited_object_detail(*args, **kwargs):
|
||||||
return object_detail(*args, **kwargs)
|
return object_detail(*args, **kwargs)
|
||||||
|
|
||||||
.. _generic view: http://www.djangoproject.com/documentation/generic_views/
|
.. _generic view: ../generic_views/
|
||||||
|
|
||||||
Permissions
|
Permissions
|
||||||
===========
|
===========
|
||||||
@ -606,7 +606,7 @@ This example model creates three custom permissions::
|
|||||||
The only thing this does is create those extra permissions when you run
|
The only thing this does is create those extra permissions when you run
|
||||||
``syncdb``.
|
``syncdb``.
|
||||||
|
|
||||||
.. _model Meta attribute: http://www.djangoproject.com/documentation/model_api/#meta-options
|
.. _model Meta attribute: ../model_api/#meta-options
|
||||||
|
|
||||||
API reference
|
API reference
|
||||||
-------------
|
-------------
|
||||||
@ -645,7 +645,7 @@ The currently logged-in user and his/her permissions are made available in the
|
|||||||
setting contains ``"django.core.context_processors.auth"``, which is default.
|
setting contains ``"django.core.context_processors.auth"``, which is default.
|
||||||
For more, see the `RequestContext docs`_.
|
For more, see the `RequestContext docs`_.
|
||||||
|
|
||||||
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-requestcontext
|
.. _RequestContext docs: ../templates_python/#subclassing-context-requestcontext
|
||||||
|
|
||||||
Users
|
Users
|
||||||
-----
|
-----
|
||||||
@ -691,7 +691,7 @@ Thus, you can check permissions in template ``{% if %}`` statements::
|
|||||||
<p>You don't have permission to do anything in the foo app.</p>
|
<p>You don't have permission to do anything in the foo app.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
.. _template context: http://www.djangoproject.com/documentation/templates_python/
|
.. _template context: ../templates_python/
|
||||||
|
|
||||||
Groups
|
Groups
|
||||||
======
|
======
|
||||||
@ -756,7 +756,7 @@ scenes, so any messages will be deleted even if you don't display them.
|
|||||||
Finally, note that this messages framework only works with users in the user
|
Finally, note that this messages framework only works with users in the user
|
||||||
database. To send messages to anonymous users, use the `session framework`_.
|
database. To send messages to anonymous users, use the `session framework`_.
|
||||||
|
|
||||||
.. _session framework: http://www.djangoproject.com/documentation/sessions/
|
.. _session framework: ../sessions/
|
||||||
|
|
||||||
Other authentication sources
|
Other authentication sources
|
||||||
============================
|
============================
|
||||||
|
@ -250,7 +250,7 @@ Additionally, ``CacheMiddleware`` automatically sets a few headers in each
|
|||||||
|
|
||||||
See the `middleware documentation`_ for more on middleware.
|
See the `middleware documentation`_ for more on middleware.
|
||||||
|
|
||||||
.. _`middleware documentation`: http://www.djangoproject.com/documentation/middleware/
|
.. _`middleware documentation`: ../middleware/
|
||||||
|
|
||||||
The per-view cache
|
The per-view cache
|
||||||
==================
|
==================
|
||||||
|
@ -122,9 +122,9 @@ Patch style
|
|||||||
* Name the patch file with a ``.diff`` extension; this will let the ticket
|
* Name the patch file with a ``.diff`` extension; this will let the ticket
|
||||||
tracker apply correct syntax highlighting, which is quite helpful.
|
tracker apply correct syntax highlighting, which is quite helpful.
|
||||||
|
|
||||||
* Put the prefix "[patch] " before the title of your ticket. This will make
|
* Check the "Has patch" box on the ticket details. This will make it
|
||||||
it obvious that the ticket includes a patch, and it will add the ticket
|
obvious that the ticket includes a patch, and it will add the ticket to
|
||||||
to the `list of tickets with patches`_.
|
the `list of tickets with patches`_.
|
||||||
|
|
||||||
* The code required to fix a problem or add a feature is an essential part
|
* The code required to fix a problem or add a feature is an essential part
|
||||||
of a patch, but it is not the only part. A good patch should also include
|
of a patch, but it is not the only part. A good patch should also include
|
||||||
@ -151,24 +151,70 @@ Unfortunately, not all bug reports in the `ticket tracker`_ provide all
|
|||||||
the `required details`_. A number of tickets have patches, but those patches
|
the `required details`_. A number of tickets have patches, but those patches
|
||||||
don't meet all the requirements of a `good patch`_.
|
don't meet all the requirements of a `good patch`_.
|
||||||
|
|
||||||
One way to help out is to *triage* bugs that have been reported by other users.
|
One way to help out is to *triage* bugs that have been reported by other
|
||||||
Pick an open ticket that is missing some details, and try to replicate the
|
users. A couple of dedicated volunteers work on this regularly, but more help
|
||||||
problem. Fill in the missing pieces of the report. If the ticket doesn't have
|
is always appreciated.
|
||||||
a patch, create one.
|
|
||||||
|
|
||||||
Once you've completed all the missing details on the ticket and you have a
|
Most of the workflow is based around the concept of a ticket's "triage stage".
|
||||||
patch with all the required features, e-mail `django-developers`_. Indicate
|
This stage describes where in its lifetime a given ticket is at any time.
|
||||||
that you have triaged a ticket, and recommend a course of action for dealing
|
Along with a handful of flags, this field easily tells us what and who each
|
||||||
with that ticket.
|
ticket is waiting on.
|
||||||
|
|
||||||
At first, this may require you to be persistent. If you find that your triaged
|
Since a picture is worth a thousand words, let's start there:
|
||||||
ticket still isn't getting attention, occasional polite requests for eyeballs
|
|
||||||
to look at your ticket may be necessary. However, as you earn a reputation for
|
.. image:: http://media.djangoproject.com/img/doc/djangotickets.png
|
||||||
quality triage work, you should find that it is easier to get the developers'
|
:height: 451
|
||||||
attention.
|
:width: 590
|
||||||
|
:alt: Django's ticket workflow
|
||||||
|
|
||||||
|
We've got two roles here:
|
||||||
|
|
||||||
|
* Core developers: people with commit access who make the decisions and
|
||||||
|
write the bulk of the code.
|
||||||
|
|
||||||
|
* Ticket triagers: community members who keep track of tickets, making
|
||||||
|
sure the tickets are always categorized correctly.
|
||||||
|
|
||||||
|
Second, note the four triage stages:
|
||||||
|
|
||||||
|
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
|
||||||
|
examine the ticket and move it along.
|
||||||
|
|
||||||
|
2. "Design decision needed" means "this concept requires a design
|
||||||
|
decision," which should be discussed either in the ticket comments or on
|
||||||
|
django-developers.
|
||||||
|
|
||||||
|
3. Once a ticket is ruled to be approved for fixing, it's moved into the
|
||||||
|
"Accepted" stage. This stage is where all the real work gets done.
|
||||||
|
|
||||||
|
4. If a ticket has an associated patch (see below), a triager will review the
|
||||||
|
patch. If the patch is complete, it'll be marked as "ready for checkin" so
|
||||||
|
that a core developer knows to review and check in the patches.
|
||||||
|
|
||||||
|
The second part of this workflow involves a set of flags the describe what the
|
||||||
|
ticket has or needs in order to be "ready for checkin":
|
||||||
|
|
||||||
|
"Has patch"
|
||||||
|
The means the ticket has an associated patch_. These will be
|
||||||
|
reviewed to see if the patch is "good".
|
||||||
|
|
||||||
|
"Needs documentation"
|
||||||
|
This flag is used for tickets with patches that need associated
|
||||||
|
documentation. Complete documentation of features is a prerequisite
|
||||||
|
before we can check a fix into the codebase.
|
||||||
|
|
||||||
|
"Needs tests"
|
||||||
|
This flags the patch as needing associated unit tests. Again, this is a
|
||||||
|
required part of a valid patch.
|
||||||
|
|
||||||
|
"Patch needs improvement"
|
||||||
|
This flag means that although the ticket *has* a patch, it's not quite
|
||||||
|
ready for checkin. This could mean the patch no longer applies
|
||||||
|
cleanly, or that the code doesn't live up to our standards.
|
||||||
|
|
||||||
.. _required details: `Reporting bugs`_
|
.. _required details: `Reporting bugs`_
|
||||||
.. _good patch: `Patch style`_
|
.. _good patch: `Patch style`_
|
||||||
|
.. _patch: `Submitting patches`_
|
||||||
|
|
||||||
Submitting and maintaining translations
|
Submitting and maintaining translations
|
||||||
=======================================
|
=======================================
|
||||||
@ -186,7 +232,7 @@ translated, here's what to do:
|
|||||||
`i18n documentation`_.
|
`i18n documentation`_.
|
||||||
|
|
||||||
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
|
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
|
||||||
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
|
.. _i18n documentation: ../i18n/
|
||||||
|
|
||||||
Coding style
|
Coding style
|
||||||
============
|
============
|
||||||
@ -311,7 +357,7 @@ The Django tests all use the testing infrastructure that ships with Django for
|
|||||||
testing applications. See `Testing Django applications`_ for an explanation of
|
testing applications. See `Testing Django applications`_ for an explanation of
|
||||||
how to write new tests.
|
how to write new tests.
|
||||||
|
|
||||||
.. _Testing Django applications: http://www.djangoproject.com/documentation/testing/
|
.. _Testing Django applications: ../testing/
|
||||||
|
|
||||||
Running the unit tests
|
Running the unit tests
|
||||||
----------------------
|
----------------------
|
||||||
@ -548,8 +594,7 @@ requests for commit access are potential flame-war starters, and will be ignored
|
|||||||
.. _search the tracker: http://code.djangoproject.com/search
|
.. _search the tracker: http://code.djangoproject.com/search
|
||||||
.. _django-users: http://groups.google.com/group/django-users
|
.. _django-users: http://groups.google.com/group/django-users
|
||||||
.. _`#django`: irc://irc.freenode.net/django
|
.. _`#django`: irc://irc.freenode.net/django
|
||||||
.. _list of tickets with patches: http://code.djangoproject.com/report/12
|
.. _list of tickets with patches: http://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority
|
||||||
.. _PEP 8: http://www.python.org/peps/pep-0008.html
|
.. _PEP 8: http://www.python.org/peps/pep-0008.html
|
||||||
.. _i18n documentation: http://www.djangoproject.com/documentation/i18n/
|
|
||||||
.. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n
|
.. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n
|
||||||
.. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases
|
.. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases
|
||||||
|
@ -526,6 +526,21 @@ Examples::
|
|||||||
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
|
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
|
||||||
[datetime.datetime(2005, 3, 20)]
|
[datetime.datetime(2005, 3, 20)]
|
||||||
|
|
||||||
|
``none()``
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Returns an ``EmptyQuerySet`` -- a ``QuerySet`` that always evaluates to
|
||||||
|
an empty list. This can be used in cases where you know that you should
|
||||||
|
return an empty result set and your caller is expecting a ``QuerySet``
|
||||||
|
object (instead of returning an empty list, for example.)
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
>>> Entry.objects.none()
|
||||||
|
[]
|
||||||
|
|
||||||
``select_related()``
|
``select_related()``
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ code.
|
|||||||
|
|
||||||
This is the philosophy behind `template inheritance`_.
|
This is the philosophy behind `template inheritance`_.
|
||||||
|
|
||||||
.. _template inheritance: http://www.djangoproject.com/documentation/templates/#template-inheritance
|
.. _template inheritance: ../templates/#template-inheritance
|
||||||
|
|
||||||
Be decoupled from HTML
|
Be decoupled from HTML
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -53,7 +53,7 @@ Prints the admin-index template snippet for the given appnames.
|
|||||||
Use admin-index template snippets if you want to customize the look and feel of
|
Use admin-index template snippets if you want to customize the look and feel of
|
||||||
your admin's index page. See `Tutorial 2`_ for more information.
|
your admin's index page. See `Tutorial 2`_ for more information.
|
||||||
|
|
||||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
.. _Tutorial 2: ../tutorial2/
|
||||||
|
|
||||||
createcachetable [tablename]
|
createcachetable [tablename]
|
||||||
----------------------------
|
----------------------------
|
||||||
@ -61,7 +61,7 @@ createcachetable [tablename]
|
|||||||
Creates a cache table named ``tablename`` for use with the database cache
|
Creates a cache table named ``tablename`` for use with the database cache
|
||||||
backend. See the `cache documentation`_ for more information.
|
backend. See the `cache documentation`_ for more information.
|
||||||
|
|
||||||
.. _cache documentation: http://www.djangoproject.com/documentation/cache/
|
.. _cache documentation: ../cache/
|
||||||
|
|
||||||
dbshell
|
dbshell
|
||||||
-------
|
-------
|
||||||
@ -190,7 +190,7 @@ By default, the development server doesn't serve any static files for your site
|
|||||||
you want to configure Django to serve static media, read the `serving static files`_
|
you want to configure Django to serve static media, read the `serving static files`_
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
.. _serving static files: http://www.djangoproject.com/documentation/static_files/
|
.. _serving static files: ../static_files/
|
||||||
|
|
||||||
Turning off auto-reload
|
Turning off auto-reload
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -253,6 +253,8 @@ files are piped directly into the database after all of the models'
|
|||||||
table-creation statements have been executed. Use this SQL hook to populate
|
table-creation statements have been executed. Use this SQL hook to populate
|
||||||
tables with any necessary initial records, SQL functions or test data.
|
tables with any necessary initial records, SQL functions or test data.
|
||||||
|
|
||||||
|
Note that the order in which the SQL files are processed is undefined.
|
||||||
|
|
||||||
sqlreset [appname appname ...]
|
sqlreset [appname appname ...]
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
|
@ -101,9 +101,9 @@ The "From:" header of the e-mail will be the value of the `SERVER_EMAIL setting`
|
|||||||
|
|
||||||
This method exists for convenience and readability.
|
This method exists for convenience and readability.
|
||||||
|
|
||||||
.. _ADMINS setting: http://www.djangoproject.com/documentation/settings/#admins
|
.. _ADMINS setting: ../settings/#admins
|
||||||
.. _EMAIL_SUBJECT_PREFIX setting: http://www.djangoproject.com/documentation/settings/#email-subject-prefix
|
.. _EMAIL_SUBJECT_PREFIX setting: ../settings/#email-subject-prefix
|
||||||
.. _SERVER_EMAIL setting: http://www.djangoproject.com/documentation/settings/#server-email
|
.. _SERVER_EMAIL setting: ../settings/#server-email
|
||||||
|
|
||||||
mail_managers() function
|
mail_managers() function
|
||||||
========================
|
========================
|
||||||
@ -114,7 +114,7 @@ Here's the definition::
|
|||||||
|
|
||||||
mail_managers(subject, message, fail_silently=False)
|
mail_managers(subject, message, fail_silently=False)
|
||||||
|
|
||||||
.. _MANAGERS setting: http://www.djangoproject.com/documentation/settings/#managers
|
.. _MANAGERS setting: ../settings/#managers
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
========
|
========
|
||||||
|
24
docs/faq.txt
24
docs/faq.txt
@ -63,7 +63,7 @@ at any level -- database servers, caching servers or Web/application servers.
|
|||||||
The framework cleanly separates components such as its database layer and
|
The framework cleanly separates components such as its database layer and
|
||||||
application layer. And it ships with a simple-yet-powerful `cache framework`_.
|
application layer. And it ships with a simple-yet-powerful `cache framework`_.
|
||||||
|
|
||||||
.. _`cache framework`: http://www.djangoproject.com/documentation/cache/
|
.. _`cache framework`: ../cache/
|
||||||
|
|
||||||
Who's behind this?
|
Who's behind this?
|
||||||
------------------
|
------------------
|
||||||
@ -191,7 +191,7 @@ Like we said: We're picky.
|
|||||||
|
|
||||||
We've documented our philosophies on the `design philosophies page`_.
|
We've documented our philosophies on the `design philosophies page`_.
|
||||||
|
|
||||||
.. _design philosophies page: http://www.djangoproject.com/documentation/design_philosophies/
|
.. _design philosophies page: ../design_philosophies/
|
||||||
|
|
||||||
Do you have any of those nifty "screencast" things?
|
Do you have any of those nifty "screencast" things?
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
@ -277,9 +277,9 @@ How do I get started?
|
|||||||
run into trouble.
|
run into trouble.
|
||||||
|
|
||||||
.. _`Download the code`: http://www.djangoproject.com/download/
|
.. _`Download the code`: http://www.djangoproject.com/download/
|
||||||
.. _`installation guide`: http://www.djangoproject.com/documentation/install/
|
.. _`installation guide`: ../install/
|
||||||
.. _tutorial: http://www.djangoproject.com/documentation/tutorial1/
|
.. _tutorial: ../tutorial1/
|
||||||
.. _documentation: http://www.djangoproject.com/documentation/
|
.. _documentation: ../
|
||||||
.. _ask questions: http://www.djangoproject.com/community/
|
.. _ask questions: http://www.djangoproject.com/community/
|
||||||
|
|
||||||
How do I fix the "install a later version of setuptools" error?
|
How do I fix the "install a later version of setuptools" error?
|
||||||
@ -337,7 +337,7 @@ If you just want to play around and develop things on your local computer, use
|
|||||||
the development Web server that comes with Django. Things should Just Work.
|
the development Web server that comes with Django. Things should Just Work.
|
||||||
|
|
||||||
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
||||||
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/
|
.. _How to use Django with FastCGI: ../fastcgi/
|
||||||
.. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
.. _server arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
||||||
|
|
||||||
How do I install mod_python on Windows?
|
How do I install mod_python on Windows?
|
||||||
@ -464,7 +464,7 @@ Can I use Django with a pre-existing database?
|
|||||||
|
|
||||||
Yes. See `Integrating with a legacy database`_.
|
Yes. See `Integrating with a legacy database`_.
|
||||||
|
|
||||||
.. _`Integrating with a legacy database`: http://www.djangoproject.com/documentation/legacy_databases/
|
.. _`Integrating with a legacy database`: ../legacy_databases/
|
||||||
|
|
||||||
If I make changes to a model, how do I update the database?
|
If I make changes to a model, how do I update the database?
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
@ -511,7 +511,7 @@ type, create an initial data file and put something like this in it::
|
|||||||
As explained in the `SQL initial data file`_ documentation, this SQL file can
|
As explained in the `SQL initial data file`_ documentation, this SQL file can
|
||||||
contain arbitrary SQL, so you can make any sorts of changes you need to make.
|
contain arbitrary SQL, so you can make any sorts of changes you need to make.
|
||||||
|
|
||||||
.. _SQL initial data file: http://www.djangoproject.com/documentation/model_api/#providing-initial-sql-data
|
.. _SQL initial data file: ../model_api/#providing-initial-sql-data
|
||||||
|
|
||||||
Why is Django leaking memory?
|
Why is Django leaking memory?
|
||||||
-----------------------------
|
-----------------------------
|
||||||
@ -592,7 +592,7 @@ My admin-site CSS and images showed up fine using the development server, but th
|
|||||||
See `serving the admin files`_ in the "How to use Django with mod_python"
|
See `serving the admin files`_ in the "How to use Django with mod_python"
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
.. _serving the admin files: http://www.djangoproject.com/documentation/modpython/#serving-the-admin-files
|
.. _serving the admin files: ../modpython/#serving-the-admin-files
|
||||||
|
|
||||||
My "list_filter" contains a ManyToManyField, but the filter doesn't display.
|
My "list_filter" contains a ManyToManyField, but the filter doesn't display.
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
@ -630,7 +630,7 @@ site is built using semantic HTML and plenty of CSS hooks, so any changes you'd
|
|||||||
like to make should be possible by editing the stylesheet. We've got a
|
like to make should be possible by editing the stylesheet. We've got a
|
||||||
`guide to the CSS used in the admin`_ to get you started.
|
`guide to the CSS used in the admin`_ to get you started.
|
||||||
|
|
||||||
.. _`guide to the CSS used in the admin`: http://www.djangoproject.com/documentation/admin_css/
|
.. _`guide to the CSS used in the admin`: ../admin_css/
|
||||||
|
|
||||||
How do I create users without having to edit password hashes?
|
How do I create users without having to edit password hashes?
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
@ -640,7 +640,7 @@ development version, where this problem was fixed on Aug. 4, 2006.
|
|||||||
|
|
||||||
You can also use the Python API. See `creating users`_ for full info.
|
You can also use the Python API. See `creating users`_ for full info.
|
||||||
|
|
||||||
.. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users
|
.. _creating users: ../authentication/#creating-users
|
||||||
|
|
||||||
Contributing code
|
Contributing code
|
||||||
=================
|
=================
|
||||||
@ -651,7 +651,7 @@ How can I get started contributing code to Django?
|
|||||||
Thanks for asking! We've written an entire document devoted to this question.
|
Thanks for asking! We've written an entire document devoted to this question.
|
||||||
It's titled `Contributing to Django`_.
|
It's titled `Contributing to Django`_.
|
||||||
|
|
||||||
.. _Contributing to Django: http://www.djangoproject.com/documentation/contributing/
|
.. _Contributing to Django: ../contributing/
|
||||||
|
|
||||||
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
|
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
|
||||||
--------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------
|
||||||
|
@ -17,7 +17,7 @@ served with no startup time. Unlike mod_python (or `mod_perl`_), a FastCGI
|
|||||||
process doesn't run inside the Web server process, but in a separate,
|
process doesn't run inside the Web server process, but in a separate,
|
||||||
persistent process.
|
persistent process.
|
||||||
|
|
||||||
.. _current preferred setup: http://www.djangoproject.com/documentation/modpython/
|
.. _current preferred setup: ../modpython/
|
||||||
.. _Apache: http://httpd.apache.org/
|
.. _Apache: http://httpd.apache.org/
|
||||||
.. _mod_python: http://www.modpython.org/
|
.. _mod_python: http://www.modpython.org/
|
||||||
.. _mod_perl: http://perl.apache.org/
|
.. _mod_perl: http://perl.apache.org/
|
||||||
|
@ -29,8 +29,8 @@ To install the flatpages app, follow these steps:
|
|||||||
to your MIDDLEWARE_CLASSES_ setting.
|
to your MIDDLEWARE_CLASSES_ setting.
|
||||||
3. Run the command ``manage.py syncdb``.
|
3. Run the command ``manage.py syncdb``.
|
||||||
|
|
||||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||||
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes
|
.. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
|
||||||
|
|
||||||
How it works
|
How it works
|
||||||
============
|
============
|
||||||
@ -63,9 +63,9 @@ resort.
|
|||||||
|
|
||||||
For more on middleware, read the `middleware docs`_.
|
For more on middleware, read the `middleware docs`_.
|
||||||
|
|
||||||
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id
|
.. _SITE_ID: ../settings/#site-id
|
||||||
.. _RequestContext: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
|
.. _RequestContext: ../templates_python/#subclassing-context-djangocontext
|
||||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware docs: ../middleware/
|
||||||
|
|
||||||
How to add, change and delete flatpages
|
How to add, change and delete flatpages
|
||||||
=======================================
|
=======================================
|
||||||
@ -84,9 +84,9 @@ Flatpages are represented by a standard `Django model`_, which lives in
|
|||||||
`django/contrib/flatpages/models.py`_. You can access flatpage objects via the
|
`django/contrib/flatpages/models.py`_. You can access flatpage objects via the
|
||||||
`Django database API`_.
|
`Django database API`_.
|
||||||
|
|
||||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
.. _Django model: ../model_api/
|
||||||
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
|
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
|
||||||
.. _Django database API: http://www.djangoproject.com/documentation/db_api/
|
.. _Django database API: ../db_api/
|
||||||
|
|
||||||
Flatpage templates
|
Flatpage templates
|
||||||
==================
|
==================
|
||||||
|
@ -14,7 +14,7 @@ use the django.newforms system, which we have begun to document in the
|
|||||||
If you have legacy form/manipulator code, read the "Migration plan" section in
|
If you have legacy form/manipulator code, read the "Migration plan" section in
|
||||||
that document to understand how we're making the switch.
|
that document to understand how we're making the switch.
|
||||||
|
|
||||||
.. _newforms documentation: http://www.djangoproject.com/documentation/newforms/
|
.. _newforms documentation: ../newforms/
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
============
|
============
|
||||||
@ -665,6 +665,6 @@ fails. If no message is passed in, a default message is used.
|
|||||||
the executable specified in the ``JING_PATH`` setting (see the settings_
|
the executable specified in the ``JING_PATH`` setting (see the settings_
|
||||||
document for more details).
|
document for more details).
|
||||||
|
|
||||||
.. _`generic views`: http://www.djangoproject.com/documentation/generic_views/
|
.. _`generic views`: ../generic_views/
|
||||||
.. _`models API`: http://www.djangoproject.com/documentation/model_api/
|
.. _`models API`: ../model_api/
|
||||||
.. _settings: http://www.djangoproject.com/documentation/settings/
|
.. _settings: ../settings/
|
||||||
|
@ -71,7 +71,7 @@ are first evaluated, so if you want to pass in a QuerySet via
|
|||||||
``extra_context`` that is always fresh you need to wrap it in a function or
|
``extra_context`` that is always fresh you need to wrap it in a function or
|
||||||
lambda that returns the QuerySet.
|
lambda that returns the QuerySet.
|
||||||
|
|
||||||
.. _database API docs: http://www.djangoproject.com/documentation/db_api/
|
.. _database API docs: ../db_api/
|
||||||
|
|
||||||
"Simple" generic views
|
"Simple" generic views
|
||||||
======================
|
======================
|
||||||
@ -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:
|
``<app_label>/<model_name>_archive.html`` by default, where:
|
||||||
|
|
||||||
* ``<model_name>`` is your model's name in all lowercase. For a model
|
* ``<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
|
* ``<app_label>`` is the right-most part of the full Python path to
|
||||||
your model's app. For example, if your model lives in
|
your model's app. For example, if your model lives in
|
||||||
``apps/blog/models.py``, that'd be ``blog``.
|
``apps/blog/models.py``, that'd be ``blog``.
|
||||||
|
|
||||||
**Template context:**
|
**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
|
by ``date_field``. For example, if ``num_latest`` is ``10``, then
|
||||||
``latest`` will be a list of the latest 10 objects in ``queryset``.
|
``latest`` will be a list of the latest 10 objects in ``queryset``.
|
||||||
|
|
||||||
.. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
|
.. _RequestContext docs: ../templates_python/#subclassing-context-djangocontext
|
||||||
|
|
||||||
``django.views.generic.date_based.archive_year``
|
``django.views.generic.date_based.archive_year``
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
@ -266,9 +266,9 @@ to ``True``.
|
|||||||
the view's template. See the `RequestContext docs`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
|
||||||
* ``template_object_name``: Designates the name of the template variable
|
* ``template_object_name``: Designates the name of the template variable
|
||||||
to use in the template context. By default, this is ``'object'``. The
|
to use in the template context. By default, this is ``'object'``. The
|
||||||
view will append ``'_list'`` to the value of this parameter in
|
view will append ``'_list'`` to the value of this parameter in
|
||||||
determining the variable's name.
|
determining the variable's name.
|
||||||
|
|
||||||
* ``make_object_list``: A boolean specifying whether to retrieve the full
|
* ``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``,
|
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`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
|
||||||
* ``template_object_name``: Designates the name of the template variable
|
* ``template_object_name``: Designates the name of the template variable
|
||||||
to use in the template context. By default, this is ``'object'``. The
|
to use in the template context. By default, this is ``'object'``. The
|
||||||
view will append ``'_list'`` to the value of this parameter in
|
view will append ``'_list'`` to the value of this parameter in
|
||||||
determining the variable's name.
|
determining the variable's name.
|
||||||
|
|
||||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
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`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
|
||||||
* ``template_object_name``: Designates the name of the template variable
|
* ``template_object_name``: Designates the name of the template variable
|
||||||
to use in the template context. By default, this is ``'object'``. The
|
to use in the template context. By default, this is ``'object'``. The
|
||||||
view will append ``'_list'`` to the value of this parameter in
|
view will append ``'_list'`` to the value of this parameter in
|
||||||
determining the variable's name.
|
determining the variable's name.
|
||||||
|
|
||||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
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`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
|
||||||
* ``template_object_name``: Designates the name of the template variable
|
* ``template_object_name``: Designates the name of the template variable
|
||||||
to use in the template context. By default, this is ``'object'``. The
|
to use in the template context. By default, this is ``'object'``. The
|
||||||
view will append ``'_list'`` to the value of this parameter in
|
view will append ``'_list'`` to the value of this parameter in
|
||||||
determining the variable's name.
|
determining the variable's name.
|
||||||
|
|
||||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
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`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
|
||||||
* ``template_object_name``: Designates the name of the template variable
|
* ``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
|
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
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`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
|
||||||
* ``template_object_name``: Designates the name of the template variable
|
* ``template_object_name``: Designates the name of the template variable
|
||||||
to use in the template context. By default, this is ``'object'``. The
|
to use in the template context. By default, this is ``'object'``. The
|
||||||
view will append ``'_list'`` to the value of this parameter in
|
view will append ``'_list'`` to the value of this parameter in
|
||||||
determining the variable's name.
|
determining the variable's name.
|
||||||
|
|
||||||
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
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`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
|
||||||
* ``template_object_name``: Designates the name of the template variable
|
* ``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
|
* ``mimetype``: The MIME type to use for the resulting document. Defaults
|
||||||
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
|
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
|
See the `manipulator and formfield documentation`_ for more information
|
||||||
about using ``FormWrapper`` objects in templates.
|
about using ``FormWrapper`` objects in templates.
|
||||||
|
|
||||||
.. _authentication system: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication system: ../authentication/
|
||||||
.. _manipulator and formfield documentation: http://www.djangoproject.com/documentation/forms/
|
.. _manipulator and formfield documentation: ../forms/
|
||||||
|
|
||||||
``django.views.generic.create_update.update_object``
|
``django.views.generic.create_update.update_object``
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
@ -973,7 +973,7 @@ object. This uses the automatic manipulators that come with Django models.
|
|||||||
the view's template. See the `RequestContext docs`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
|
||||||
* ``template_object_name``: Designates the name of the template variable
|
* ``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:**
|
**Template name:**
|
||||||
|
|
||||||
@ -1054,7 +1054,7 @@ contain a form that POSTs to the same URL.
|
|||||||
the view's template. See the `RequestContext docs`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
|
||||||
* ``template_object_name``: Designates the name of the template variable
|
* ``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:**
|
**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'``
|
You'll probably also want to remove ``'django.core.context_processors.i18n'``
|
||||||
from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
|
from your ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
|
||||||
|
|
||||||
.. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n
|
.. _documentation for USE_I18N: ../settings/#use-i18n
|
||||||
|
|
||||||
How to specify translation strings
|
How to specify translation strings
|
||||||
==================================
|
==================================
|
||||||
@ -175,7 +175,7 @@ class, though::
|
|||||||
verbose_name = _('my thing')
|
verbose_name = _('my thing')
|
||||||
verbose_name_plural = _('mythings')
|
verbose_name_plural = _('mythings')
|
||||||
|
|
||||||
.. _Django models: http://www.djangoproject.com/documentation/model_api/
|
.. _Django models: ../model_api/
|
||||||
|
|
||||||
Pluralization
|
Pluralization
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@ -274,7 +274,7 @@ translation string. Example::
|
|||||||
In this case, both the tag and the filter will see the already-translated
|
In this case, both the tag and the filter will see the already-translated
|
||||||
string, so they don't need to be aware of translations.
|
string, so they don't need to be aware of translations.
|
||||||
|
|
||||||
.. _Django templates: http://www.djangoproject.com/documentation/templates_python/
|
.. _Django templates: ../templates_python/
|
||||||
|
|
||||||
How to create language files
|
How to create language files
|
||||||
============================
|
============================
|
||||||
@ -394,7 +394,7 @@ That's it. Your translations are ready for use.
|
|||||||
please let us know! See `Submitting and maintaining translations`_ for
|
please let us know! See `Submitting and maintaining translations`_ for
|
||||||
the steps to take.
|
the steps to take.
|
||||||
|
|
||||||
.. _Submitting and maintaining translations: http://www.djangoproject.com/documentation/contributing/
|
.. _Submitting and maintaining translations: ../contributing/
|
||||||
|
|
||||||
How Django discovers language preference
|
How Django discovers language preference
|
||||||
========================================
|
========================================
|
||||||
@ -472,7 +472,7 @@ Notes:
|
|||||||
selection to German and English (and any sublanguage, like de-ch or
|
selection to German and English (and any sublanguage, like de-ch or
|
||||||
en-us).
|
en-us).
|
||||||
|
|
||||||
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
|
.. _LANGUAGES setting: ../settings/#languages
|
||||||
|
|
||||||
* If you define a custom ``LANGUAGES`` setting, as explained in the
|
* If you define a custom ``LANGUAGES`` setting, as explained in the
|
||||||
previous bullet, it's OK to mark the languages as translation strings
|
previous bullet, it's OK to mark the languages as translation strings
|
||||||
@ -530,10 +530,10 @@ Note that, with static (middleware-less) translation, the language is in
|
|||||||
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
|
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
|
||||||
in ``request.LANGUAGE_CODE``.
|
in ``request.LANGUAGE_CODE``.
|
||||||
|
|
||||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
.. _settings file: ../settings/
|
||||||
.. _middleware documentation: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware documentation: ../middleware/
|
||||||
.. _session: http://www.djangoproject.com/documentation/sessions/
|
.. _session: ../sessions/
|
||||||
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
.. _request object: ../request_response/#httprequest-objects
|
||||||
|
|
||||||
The ``set_language`` redirect view
|
The ``set_language`` redirect view
|
||||||
==================================
|
==================================
|
||||||
@ -599,7 +599,7 @@ message file. The choice is yours.
|
|||||||
of the settings file to determine this, and a settings file doesn't exist
|
of the settings file to determine this, and a settings file doesn't exist
|
||||||
if you're manually configuring your settings.)
|
if you're manually configuring your settings.)
|
||||||
|
|
||||||
.. _settings documentation: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
|
.. _settings documentation: ../settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||||
|
|
||||||
All message file repositories are structured the same way. They are:
|
All message file repositories are structured the same way. They are:
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ each platform.
|
|||||||
.. _Apache: http://httpd.apache.org/
|
.. _Apache: http://httpd.apache.org/
|
||||||
.. _mod_python: http://www.modpython.org/
|
.. _mod_python: http://www.modpython.org/
|
||||||
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
.. _WSGI: http://www.python.org/peps/pep-0333.html
|
||||||
.. _How to use Django with mod_python: http://www.djangoproject.com/documentation/modpython/
|
.. _How to use Django with mod_python: ../modpython/
|
||||||
.. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
.. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements
|
||||||
|
|
||||||
Get your database running
|
Get your database running
|
||||||
@ -113,14 +113,14 @@ latest bug fixes and improvements, follow these instructions:
|
|||||||
|
|
||||||
svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django
|
svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django
|
||||||
|
|
||||||
4. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
|
3. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
|
||||||
system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
|
system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
|
||||||
(Windows). This step simply lets you type ``django-admin.py`` from within
|
(Windows). This step simply lets you type ``django-admin.py`` from within
|
||||||
any directory, rather than having to qualify the command with the full path
|
any directory, rather than having to qualify the command with the full path
|
||||||
to the file.
|
to the file.
|
||||||
|
|
||||||
You *don't* have to run ``python setup.py install``, because that command
|
You *don't* have to run ``python setup.py install``, because that command
|
||||||
takes care of steps 3 and 4 for you.
|
takes care of steps 2 and 3 for you.
|
||||||
|
|
||||||
When you want to update your copy of the Django source code, just run the
|
When you want to update your copy of the Django source code, just run the
|
||||||
command ``svn update`` from within the ``django`` directory. When you do this,
|
command ``svn update`` from within the ``django`` directory. When you do this,
|
||||||
|
@ -9,7 +9,7 @@ utilities to automate as much of this process as possible.
|
|||||||
This document assumes you know the Django basics, as covered in the
|
This document assumes you know the Django basics, as covered in the
|
||||||
`official tutorial`_.
|
`official tutorial`_.
|
||||||
|
|
||||||
.. _official tutorial: http://www.djangoproject.com/documentation/tutorial1/
|
.. _official tutorial: ../tutorial1/
|
||||||
|
|
||||||
Give Django your database parameters
|
Give Django your database parameters
|
||||||
====================================
|
====================================
|
||||||
@ -25,13 +25,13 @@ what the name of the database is. Do that by editing these settings in your
|
|||||||
* `DATABASE_HOST`_
|
* `DATABASE_HOST`_
|
||||||
* `DATABASE_PORT`_
|
* `DATABASE_PORT`_
|
||||||
|
|
||||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
.. _settings file: ../settings/
|
||||||
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name
|
.. _DATABASE_NAME: ../settings/#database-name
|
||||||
.. _DATABASE_ENGINE: http://www.djangoproject.com/documentation/settings/#database-engine
|
.. _DATABASE_ENGINE: ../settings/#database-engine
|
||||||
.. _DATABASE_USER: http://www.djangoproject.com/documentation/settings/#database-user
|
.. _DATABASE_USER: ../settings/#database-user
|
||||||
.. _DATABASE_PASSWORD: http://www.djangoproject.com/documentation/settings/#database-password
|
.. _DATABASE_PASSWORD: ../settings/#database-password
|
||||||
.. _DATABASE_HOST: http://www.djangoproject.com/documentation/settings/#database-host
|
.. _DATABASE_HOST: ../settings/#database-host
|
||||||
.. _DATABASE_PORT: http://www.djangoproject.com/documentation/settings/#database-port
|
.. _DATABASE_PORT: ../settings/#database-port
|
||||||
|
|
||||||
Auto-generate the models
|
Auto-generate the models
|
||||||
========================
|
========================
|
||||||
@ -52,7 +52,7 @@ Once you've cleaned up your models, name the file ``models.py`` and put it in
|
|||||||
the Python package that holds your app. Then add the app to your
|
the Python package that holds your app. Then add the app to your
|
||||||
``INSTALLED_APPS`` setting.
|
``INSTALLED_APPS`` setting.
|
||||||
|
|
||||||
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin.py documentation: ../django_admin/
|
||||||
|
|
||||||
Install the core Django tables
|
Install the core Django tables
|
||||||
==============================
|
==============================
|
||||||
|
@ -47,7 +47,7 @@ Enables site-wide cache. If this is enabled, each Django-powered page will be
|
|||||||
cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See
|
cached for as long as the ``CACHE_MIDDLEWARE_SECONDS`` setting defines. See
|
||||||
the `cache documentation`_.
|
the `cache documentation`_.
|
||||||
|
|
||||||
.. _`cache documentation`: http://www.djangoproject.com/documentation/cache/#the-per-site-cache
|
.. _`cache documentation`: ../cache/#the-per-site-cache
|
||||||
|
|
||||||
django.middleware.common.CommonMiddleware
|
django.middleware.common.CommonMiddleware
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
@ -106,9 +106,10 @@ django.middleware.http.SetRemoteAddrFromForwardedFor
|
|||||||
|
|
||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
Sets ``request['REMOTE_ADDR']`` based on ``request.['HTTP_X_FORWARDED_FOR']``,
|
Sets ``request.META['REMOTE_ADDR']`` based on
|
||||||
if the latter is set. This is useful if you're sitting behind a reverse proxy
|
``request.META['HTTP_X_FORWARDED_FOR']``, if the latter is set. This is useful
|
||||||
that causes each request's ``REMOTE_ADDR`` to be set to ``127.0.0.1``.
|
if you're sitting behind a reverse proxy that causes each request's
|
||||||
|
``REMOTE_ADDR`` to be set to ``127.0.0.1``.
|
||||||
|
|
||||||
**Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're
|
**Important note:** This does NOT validate ``HTTP_X_FORWARDED_FOR``. If you're
|
||||||
not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do
|
not behind a reverse proxy that sets ``HTTP_X_FORWARDED_FOR`` automatically, do
|
||||||
@ -122,7 +123,7 @@ django.contrib.sessions.middleware.SessionMiddleware
|
|||||||
|
|
||||||
Enables session support. See the `session documentation`_.
|
Enables session support. See the `session documentation`_.
|
||||||
|
|
||||||
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
.. _`session documentation`: ../sessions/
|
||||||
|
|
||||||
django.contrib.auth.middleware.AuthenticationMiddleware
|
django.contrib.auth.middleware.AuthenticationMiddleware
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
@ -130,7 +131,7 @@ django.contrib.auth.middleware.AuthenticationMiddleware
|
|||||||
Adds the ``user`` attribute, representing the currently-logged-in user, to
|
Adds the ``user`` attribute, representing the currently-logged-in user, to
|
||||||
every incoming ``HttpRequest`` object. See `Authentication in Web requests`_.
|
every incoming ``HttpRequest`` object. See `Authentication in Web requests`_.
|
||||||
|
|
||||||
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests
|
.. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
|
||||||
|
|
||||||
django.middleware.transaction.TransactionMiddleware
|
django.middleware.transaction.TransactionMiddleware
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
@ -146,7 +147,7 @@ the same transaction control as the view functions.
|
|||||||
|
|
||||||
See the `transaction management documentation`_.
|
See the `transaction management documentation`_.
|
||||||
|
|
||||||
.. _`transaction management documentation`: http://www.djangoproject.com/documentation/transactions/
|
.. _`transaction management documentation`: ../transactions/
|
||||||
|
|
||||||
Writing your own middleware
|
Writing your own middleware
|
||||||
===========================
|
===========================
|
||||||
|
@ -875,6 +875,10 @@ the relationship should work. All are optional:
|
|||||||
relationship, allowing ``ManyToMany`` relationships to be
|
relationship, allowing ``ManyToMany`` relationships to be
|
||||||
non-symmetrical.
|
non-symmetrical.
|
||||||
|
|
||||||
|
``db_table`` The name of the table to create for storing the many-to-many
|
||||||
|
data. If this is not provided, Django will assume a default
|
||||||
|
name based upon the names of the two tables being joined.
|
||||||
|
|
||||||
======================= ============================================================
|
======================= ============================================================
|
||||||
|
|
||||||
One-to-one relationships
|
One-to-one relationships
|
||||||
@ -1268,6 +1272,24 @@ A few special cases to note about ``list_display``:
|
|||||||
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
|
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
|
||||||
colored_name.allow_tags = True
|
colored_name.allow_tags = True
|
||||||
|
|
||||||
|
* If the string given is a method of the model that returns True or False
|
||||||
|
Django will display a pretty "on" or "off" icon if you give the method a
|
||||||
|
``boolean`` attribute whose value is ``True``.
|
||||||
|
|
||||||
|
Here's a full example model::
|
||||||
|
|
||||||
|
class Person(models.Model):
|
||||||
|
first_name = models.CharField(maxlength=50)
|
||||||
|
birthday = models.DateField()
|
||||||
|
|
||||||
|
class Admin:
|
||||||
|
list_display = ('name', 'born_in_fifties')
|
||||||
|
|
||||||
|
def born_in_fifties(self):
|
||||||
|
return self.birthday.strftime('%Y')[:3] == 5
|
||||||
|
born_in_fifties.boolean = True
|
||||||
|
|
||||||
|
|
||||||
* The ``__str__()`` method is just as valid in ``list_display`` as any
|
* The ``__str__()`` method is just as valid in ``list_display`` as any
|
||||||
other model method, so it's perfectly OK to do this::
|
other model method, so it's perfectly OK to do this::
|
||||||
|
|
||||||
@ -1390,7 +1412,10 @@ This should be set to a list of field names that will be searched whenever
|
|||||||
somebody submits a search query in that text box.
|
somebody submits a search query in that text box.
|
||||||
|
|
||||||
These fields should be some kind of text field, such as ``CharField`` or
|
These fields should be some kind of text field, such as ``CharField`` or
|
||||||
``TextField``.
|
``TextField``. You can also perform a related lookup on a ``ForeignKey`` with
|
||||||
|
the lookup API "follow" notation::
|
||||||
|
|
||||||
|
search_fields = ['foreign_key__related_fieldname']
|
||||||
|
|
||||||
When somebody does a search in the admin search box, Django splits the search
|
When somebody does a search in the admin search box, Django splits the search
|
||||||
query into words and returns all objects that contain each of the words, case
|
query into words and returns all objects that contain each of the words, case
|
||||||
|
@ -20,7 +20,7 @@ You may also be interested in `How to use Django with FastCGI`_.
|
|||||||
.. _mod_perl: http://perl.apache.org/
|
.. _mod_perl: http://perl.apache.org/
|
||||||
.. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html
|
.. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html
|
||||||
.. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html
|
.. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html
|
||||||
.. _How to use Django with FastCGI: http://www.djangoproject.com/documentation/fastcgi/
|
.. _How to use Django with FastCGI: ../fastcgi/
|
||||||
|
|
||||||
Basic configuration
|
Basic configuration
|
||||||
===================
|
===================
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
The newforms library
|
The newforms library
|
||||||
====================
|
====================
|
||||||
|
|
||||||
``django.newforms`` is a new replacement for ``django.forms``, the old Django
|
``django.newforms`` is Django's fantastic new form-handling library. It's a
|
||||||
form/manipulator/validation framework. This document explains how to use this
|
replacement for ``django.forms``, the old form/manipulator/validation
|
||||||
new form library.
|
framework. This document explains how to use this new library.
|
||||||
|
|
||||||
Migration plan
|
Migration plan
|
||||||
==============
|
==============
|
||||||
@ -51,7 +51,7 @@ too messy. The choice is yours.
|
|||||||
Overview
|
Overview
|
||||||
========
|
========
|
||||||
|
|
||||||
As the ``django.forms`` ("manipulators") system before it, ``django.newforms``
|
As with the ``django.forms`` ("manipulators") system before it, ``django.newforms``
|
||||||
is intended to handle HTML form display, validation and redisplay. It's what
|
is intended to handle HTML form display, validation and redisplay. It's what
|
||||||
you use if you want to perform server-side validation for an HTML form.
|
you use if you want to perform server-side validation for an HTML form.
|
||||||
|
|
||||||
@ -74,7 +74,9 @@ The library deals with these concepts:
|
|||||||
|
|
||||||
The library is decoupled from the other Django components, such as the database
|
The library is decoupled from the other Django components, such as the database
|
||||||
layer, views and templates. It relies only on Django settings, a couple of
|
layer, views and templates. It relies only on Django settings, a couple of
|
||||||
``django.utils`` helper functions and Django's internationalization system.
|
``django.utils`` helper functions and Django's internationalization hooks (but
|
||||||
|
you're not required to be using internationalization features to use this
|
||||||
|
library).
|
||||||
|
|
||||||
Form objects
|
Form objects
|
||||||
============
|
============
|
||||||
@ -83,8 +85,8 @@ The primary way of using the ``newforms`` library is to create a form object.
|
|||||||
Do this by subclassing ``django.newforms.Form`` and specifying the form's
|
Do this by subclassing ``django.newforms.Form`` and specifying the form's
|
||||||
fields, in a declarative style that you'll be familiar with if you've used
|
fields, in a declarative style that you'll be familiar with if you've used
|
||||||
Django database models. In this section, we'll iteratively develop a form
|
Django database models. In this section, we'll iteratively develop a form
|
||||||
object that you might to implement "contact me" functionality on your personal
|
object that you might use to implement "contact me" functionality on your
|
||||||
Web site.
|
personal Web site.
|
||||||
|
|
||||||
Start with this basic ``Form`` subclass, which we'll call ``ContactForm``::
|
Start with this basic ``Form`` subclass, which we'll call ``ContactForm``::
|
||||||
|
|
||||||
@ -101,11 +103,192 @@ fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain
|
|||||||
the different types of fields -- e.g., ``CharField`` and ``EmailField`` --
|
the different types of fields -- e.g., ``CharField`` and ``EmailField`` --
|
||||||
shortly.
|
shortly.
|
||||||
|
|
||||||
|
Creating ``Form`` instances
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
A ``Form`` instance is either **bound** or **unbound** to a set of data.
|
||||||
|
|
||||||
|
* If it's **bound** to a set of data, it's capable of validating that data
|
||||||
|
and rendering the form as HTML with the data displayed in the HTML.
|
||||||
|
|
||||||
|
* If it's **unbound**, it cannot do validation (because there's no data to
|
||||||
|
validate!), but it can still render the blank form as HTML.
|
||||||
|
|
||||||
|
To create an unbound ``Form`` instance, simply instantiate the class::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
|
||||||
|
To bind data to a form, pass the data as a dictionary as the first parameter to
|
||||||
|
your ``Form`` class constructor::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
|
||||||
|
In this dictionary, the keys are the field names, which correspond to the
|
||||||
|
attributes in your ``Form`` class. The values are the data you're trying
|
||||||
|
to validate. These will usually be strings, but there's no requirement that
|
||||||
|
they be strings; the type of data you pass depends on the ``Field``, as we'll
|
||||||
|
see in a moment.
|
||||||
|
|
||||||
|
If you need to distinguish between bound and unbound form instances at runtime,
|
||||||
|
check the value of the form's ``is_bound`` attribute::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.is_bound
|
||||||
|
False
|
||||||
|
>>> f = ContactForm({'subject': 'hello'})
|
||||||
|
>>> f.is_bound
|
||||||
|
True
|
||||||
|
|
||||||
|
Note that passing an empty dictionary creates a *bound* form with empty data::
|
||||||
|
|
||||||
|
>>> f = ContactForm({})
|
||||||
|
>>> f.is_bound
|
||||||
|
True
|
||||||
|
|
||||||
|
If you have a bound ``Form`` instance and want to change the data somehow, or
|
||||||
|
if you want to bind an unbound ``Form`` instance to some data, create another
|
||||||
|
``Form`` instance. There is no way to change data in a ``Form`` instance. Once
|
||||||
|
a ``Form`` instance has been created, you should consider its data immutable,
|
||||||
|
whether it has data or not.
|
||||||
|
|
||||||
|
Using forms to validate data
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The primary task of a ``Form`` object is to validate data. With a bound
|
||||||
|
``Form`` instance, call the ``is_valid()`` method to run validation and return
|
||||||
|
a boolean designating whether the data was valid::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
|
||||||
|
Let's try with some invalid data. In this case, ``subject`` is blank (an error,
|
||||||
|
because all fields are required by default) and ``sender`` is not a valid
|
||||||
|
e-mail address::
|
||||||
|
|
||||||
|
>>> data = {'subject': '',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'invalid e-mail address',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
False
|
||||||
|
|
||||||
|
Access the ``Form`` attribute ``errors`` to get a dictionary of error messages::
|
||||||
|
|
||||||
|
>>> f.errors
|
||||||
|
{'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']}
|
||||||
|
|
||||||
|
In this dictionary, the keys are the field names, and the values are lists of
|
||||||
|
Unicode strings representing the error messages. The error messages are stored
|
||||||
|
in lists because a field can have multiple error messages.
|
||||||
|
|
||||||
|
You can access ``errors`` without having to call ``is_valid()`` first. The
|
||||||
|
form's data will be validated the first time either you call ``is_valid()`` or
|
||||||
|
access ``errors``.
|
||||||
|
|
||||||
|
Behavior of unbound forms
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
It's meaningless to validate a form with no data, but, for the record, here's
|
||||||
|
what happens with unbound forms::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.is_valid()
|
||||||
|
False
|
||||||
|
>>> f.errors
|
||||||
|
{}
|
||||||
|
|
||||||
|
Accessing "clean" data
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Each ``Field`` in a ``Form`` class is responsible not only for validating data,
|
||||||
|
but also for "cleaning" it -- normalizing it to a consistent format. This is a
|
||||||
|
nice feature, because it allows data for a particular field to be input in
|
||||||
|
a variety of ways, always resulting in consistent output.
|
||||||
|
|
||||||
|
For example, ``DateField`` normalizes input into a Python ``datetime.date``
|
||||||
|
object. Regardless of whether you pass it a string in the format
|
||||||
|
``'1994-07-15'``, a ``datetime.date`` object or a number of other formats,
|
||||||
|
``DateField`` will always normalize it to a ``datetime.date`` object as long as
|
||||||
|
it's valid.
|
||||||
|
|
||||||
|
Once you've created a ``Form`` instance with a set of data and validated it,
|
||||||
|
you can access the clean data via the ``clean_data`` attribute of the ``Form``
|
||||||
|
object::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> f.clean_data
|
||||||
|
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||||
|
|
||||||
|
Note that any text-based field -- such as ``CharField`` or ``EmailField`` --
|
||||||
|
always cleans the input into a Unicode string. We'll cover the encoding
|
||||||
|
implications later in this document.
|
||||||
|
|
||||||
|
If your data does *not* validate, your ``Form`` instance will not have a
|
||||||
|
``clean_data`` attribute::
|
||||||
|
|
||||||
|
>>> data = {'subject': '',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'invalid e-mail address',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
False
|
||||||
|
>>> f.clean_data
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
||||||
|
|
||||||
|
``clean_data`` will always *only* contain a key for fields defined in the
|
||||||
|
``Form``, even if you pass extra data when you define the ``Form``. In this
|
||||||
|
example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
|
||||||
|
but ``clean_data`` contains only the form's fields::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True,
|
||||||
|
... 'extra_field_1': 'foo',
|
||||||
|
... 'extra_field_2': 'bar',
|
||||||
|
... 'extra_field_3': 'baz'}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> f.clean_data # Doesn't contain extra_field_1, etc.
|
||||||
|
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||||
|
|
||||||
|
Behavior of unbound forms
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
It's meaningless to request "clean" data in a form with no data, but, for the
|
||||||
|
record, here's what happens with unbound forms::
|
||||||
|
|
||||||
|
>>> f = ContactForm()
|
||||||
|
>>> f.clean_data
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
||||||
|
|
||||||
Outputting forms as HTML
|
Outputting forms as HTML
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
The first thing we can do with this is output it as HTML. To do so, instantiate
|
The second task of a ``Form`` object is to render itself as HTML. To do so,
|
||||||
it and ``print`` it::
|
simply ``print`` it::
|
||||||
|
|
||||||
>>> f = ContactForm()
|
>>> f = ContactForm()
|
||||||
>>> print f
|
>>> print f
|
||||||
@ -114,6 +297,23 @@ it and ``print`` it::
|
|||||||
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
|
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
|
||||||
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
|
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
|
||||||
|
|
||||||
|
If the form is bound to data, the HTML output will include that data
|
||||||
|
appropriately. For example, if a field is represented by an
|
||||||
|
``<input type="text">``, the data will be in the ``value`` attribute. If a
|
||||||
|
field is represented by an ``<input type="checkbox">``, then that HTML will
|
||||||
|
include ``checked="checked"`` if appropriate::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hello',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'foo@example.com',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data)
|
||||||
|
>>> print f
|
||||||
|
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr>
|
||||||
|
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr>
|
||||||
|
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></td></tr>
|
||||||
|
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
|
||||||
|
|
||||||
This default output is a two-column HTML table, with a ``<tr>`` for each field.
|
This default output is a two-column HTML table, with a ``<tr>`` for each field.
|
||||||
Notice the following:
|
Notice the following:
|
||||||
|
|
||||||
@ -125,13 +325,13 @@ Notice the following:
|
|||||||
``EmailField`` are represented by an ``<input type="text">``.
|
``EmailField`` are represented by an ``<input type="text">``.
|
||||||
``BooleanField`` is represented by an ``<input type="checkbox">``. Note
|
``BooleanField`` is represented by an ``<input type="checkbox">``. Note
|
||||||
these are merely sensible defaults; you can specify which HTML to use for
|
these are merely sensible defaults; you can specify which HTML to use for
|
||||||
a given field by using ``widgets``, which we'll explain shortly.
|
a given field by using widgets, which we'll explain shortly.
|
||||||
|
|
||||||
* The HTML ``name`` for each tag is taken directly from its attribute name
|
* The HTML ``name`` for each tag is taken directly from its attribute name
|
||||||
in the ``ContactForm`` class.
|
in the ``ContactForm`` class.
|
||||||
|
|
||||||
* The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and
|
* The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and
|
||||||
``'CC myself:'`` is generated from the field name by converting all
|
``'Cc myself:'`` is generated from the field name by converting all
|
||||||
underscores to spaces and upper-casing the first letter. Again, note
|
underscores to spaces and upper-casing the first letter. Again, note
|
||||||
these are merely sensible defaults; you can also specify labels manually.
|
these are merely sensible defaults; you can also specify labels manually.
|
||||||
|
|
||||||
@ -286,6 +486,37 @@ example, in the ``ContactForm`` example, the fields are defined in the order
|
|||||||
``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML
|
``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML
|
||||||
output, just change the order in which those fields are listed in the class.
|
output, just change the order in which those fields are listed in the class.
|
||||||
|
|
||||||
|
How errors are displayed
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you render a bound ``Form`` object, the act of rendering will automatically
|
||||||
|
run the form's validation if it hasn't already happened, and the HTML output
|
||||||
|
will include the validation errors as a ``<ul>`` near the field. The particular
|
||||||
|
positioning of the error messages depends on the output method you're using::
|
||||||
|
|
||||||
|
>>> data = {'subject': '',
|
||||||
|
... 'message': 'Hi there',
|
||||||
|
... 'sender': 'invalid e-mail address',
|
||||||
|
... 'cc_myself': True}
|
||||||
|
>>> f = ContactForm(data, auto_id=False)
|
||||||
|
>>> print f.as_table()
|
||||||
|
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr>
|
||||||
|
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr>
|
||||||
|
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul><input type="text" name="sender" value="invalid e-mail address" /></td></tr>
|
||||||
|
<tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li>
|
||||||
|
<li>Message: <input type="text" name="message" value="Hi there" /></li>
|
||||||
|
<li><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul>Sender: <input type="text" name="sender" value="invalid e-mail address" /></li>
|
||||||
|
<li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li>
|
||||||
|
>>> print f.as_p()
|
||||||
|
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
|
||||||
|
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
|
||||||
|
<p>Message: <input type="text" name="message" value="Hi there" /></p>
|
||||||
|
<p><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul></p>
|
||||||
|
<p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p>
|
||||||
|
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
|
||||||
|
|
||||||
More granular output
|
More granular output
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -322,6 +553,271 @@ The field-specific output honors the form object's ``auto_id`` setting::
|
|||||||
>>> print f['message']
|
>>> print f['message']
|
||||||
<input type="text" name="message" id="id_message" />
|
<input type="text" name="message" id="id_message" />
|
||||||
|
|
||||||
|
For a field's list of errors, access the field's ``errors`` attribute. This
|
||||||
|
is a list-like object that is displayed as an HTML ``<ul>`` when printed::
|
||||||
|
|
||||||
|
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
|
||||||
|
>>> f = ContactForm(data, auto_id=False)
|
||||||
|
>>> print f['message']
|
||||||
|
<input type="text" name="message" />
|
||||||
|
>>> f['message'].errors
|
||||||
|
[u'This field is required.']
|
||||||
|
>>> print f['message'].errors
|
||||||
|
<ul class="errorlist"><li>This field is required.</li></ul>
|
||||||
|
>>> f['subject'].errors
|
||||||
|
[]
|
||||||
|
>>> print f['subject'].errors
|
||||||
|
|
||||||
|
>>> str(f['subject'].errors)
|
||||||
|
''
|
||||||
|
|
||||||
|
Fields
|
||||||
|
======
|
||||||
|
|
||||||
|
When you create a ``Form`` class, the most important part is defining the
|
||||||
|
fields of the form. Each field has custom validation logic, along with a few
|
||||||
|
other hooks.
|
||||||
|
|
||||||
|
Although the primary way you'll use ``Field`` classes is in ``Form`` classes,
|
||||||
|
you can also instantiate them and use them directly to get a better idea of
|
||||||
|
how they work. Each ``Field`` instance has a ``clean()`` method, which takes
|
||||||
|
a single argument and either raises a ``django.newforms.ValidationError``
|
||||||
|
exception or returns the clean value::
|
||||||
|
|
||||||
|
>>> f = forms.EmailField()
|
||||||
|
>>> f.clean('foo@example.com')
|
||||||
|
u'foo@example.com'
|
||||||
|
>>> f.clean(u'foo@example.com')
|
||||||
|
u'foo@example.com'
|
||||||
|
>>> f.clean('invalid e-mail address')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid e-mail address.']
|
||||||
|
|
||||||
|
If you've used Django's old forms/validation framework, take care in noticing
|
||||||
|
this ``ValidationError`` is different than the previous ``ValidationError``.
|
||||||
|
This one lives at ``django.newforms.ValidationError`` rather than
|
||||||
|
``django.core.validators.ValidationError``.
|
||||||
|
|
||||||
|
Core field arguments
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Each ``Field`` class constructor takes at least these arguments. Some
|
||||||
|
``Field`` classes take additional, field-specific arguments, but the following
|
||||||
|
should *always* be available:
|
||||||
|
|
||||||
|
``required``
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
By default, each ``Field`` class assumes the value is required, so if you pass
|
||||||
|
an empty value -- either ``None`` or the empty string (``""``) -- then
|
||||||
|
``clean()`` will raise a ``ValidationError`` exception::
|
||||||
|
|
||||||
|
>>> f = forms.CharField()
|
||||||
|
>>> f.clean('foo')
|
||||||
|
u'foo'
|
||||||
|
>>> f.clean('')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean(' ')
|
||||||
|
u' '
|
||||||
|
>>> f.clean(0)
|
||||||
|
u'0'
|
||||||
|
>>> f.clean(True)
|
||||||
|
u'True'
|
||||||
|
>>> f.clean(False)
|
||||||
|
u'False'
|
||||||
|
|
||||||
|
To specify that a field is *not* required, pass ``required=False`` to the
|
||||||
|
``Field`` constructor::
|
||||||
|
|
||||||
|
>>> f = forms.CharField(required=False)
|
||||||
|
>>> f.clean('foo')
|
||||||
|
u'foo'
|
||||||
|
>>> f.clean('')
|
||||||
|
u''
|
||||||
|
>>> f.clean(None)
|
||||||
|
u''
|
||||||
|
>>> f.clean(0)
|
||||||
|
u'0'
|
||||||
|
>>> f.clean(True)
|
||||||
|
u'True'
|
||||||
|
>>> f.clean(False)
|
||||||
|
u'False'
|
||||||
|
|
||||||
|
If a ``Field`` has ``required=False`` and you pass ``clean()`` an empty value,
|
||||||
|
then ``clean()`` will return a *normalized* empty value rather than raising
|
||||||
|
``ValidationError``. For ``CharField``, this will be a Unicode empty string.
|
||||||
|
For other ``Field`` classes, it might be ``None``. (This varies from field to
|
||||||
|
field.)
|
||||||
|
|
||||||
|
``label``
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
The ``label`` argument lets you specify the "human-friendly" label for this
|
||||||
|
field. This is used when the ``Field`` is displayed in a ``Form``.
|
||||||
|
|
||||||
|
As explained in _`Outputting forms as HTML` above, the default label for a
|
||||||
|
``Field`` is generated from the field name by converting all underscores to
|
||||||
|
spaces and upper-casing the first letter. Specify ``label`` if that default
|
||||||
|
behavior doesn't result in an adequate label.
|
||||||
|
|
||||||
|
Here's a full example ``Form`` that implements ``label`` for two of its fields.
|
||||||
|
We've specified ``auto_id=False`` to simplify the output::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField(label='Your name')
|
||||||
|
... url = forms.URLField(label='Your Web site', required=False)
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> f = CommentForm(auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Your name:</th><td><input type="text" name="name" /></td></tr>
|
||||||
|
<tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
|
``initial``
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``initial`` argument lets you specify the initial value to use when
|
||||||
|
rendering this ``Field`` in an unbound ``Form``.
|
||||||
|
|
||||||
|
The use-case for this is when you want to display an "empty" form in which a
|
||||||
|
field is initialized to a particular value. For example::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField(initial='Your name')
|
||||||
|
... url = forms.URLField(initial='http://')
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> f = CommentForm(auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
|
You may be thinking, why not just pass a dictionary of the initial values as
|
||||||
|
data when displaying the form? Well, if you do that, you'll trigger validation,
|
||||||
|
and the HTML output will include any validation errors::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField()
|
||||||
|
... url = forms.URLField()
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> default_data = {'name': 'Your name', 'url': 'http://'}
|
||||||
|
>>> f = CommentForm(default_data, auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
|
This is why ``initial`` values are only displayed for unbound forms. For bound
|
||||||
|
forms, the HTML output will use the bound data.
|
||||||
|
|
||||||
|
Also note that ``initial`` values are *not* used as "fallback" data in
|
||||||
|
validation if a particular field's value is not given. ``initial`` values are
|
||||||
|
*only* intended for initial form display::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField(initial='Your name')
|
||||||
|
... url = forms.URLField(initial='http://')
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> data = {'name': '', 'url': '', 'comment': 'Foo'}
|
||||||
|
>>> f = CommentForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
False
|
||||||
|
# The form does *not* fall back to using the initial values.
|
||||||
|
>>> f.errors
|
||||||
|
{'url': [u'This field is required.'], 'name': [u'This field is required.']}
|
||||||
|
|
||||||
|
``widget``
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``widget`` argument lets you specify a ``Widget`` class to use when
|
||||||
|
rendering this ``Field``. See _`Widgets` below for more information.
|
||||||
|
|
||||||
|
``help_text``
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``help_text`` argument lets you specify descriptive text for this
|
||||||
|
``Field``. If you provide ``help_text``, it will be displayed next to the
|
||||||
|
``Field`` when the ``Field`` is rendered in a ``Form``.
|
||||||
|
|
||||||
|
Here's a full example ``Form`` that implements ``help_text`` for two of its
|
||||||
|
fields. We've specified ``auto_id=False`` to simplify the output::
|
||||||
|
|
||||||
|
>>> class HelpTextContactForm(forms.Form):
|
||||||
|
... subject = forms.CharField(max_length=100, help_text='100 characters max.')
|
||||||
|
... message = forms.CharField()
|
||||||
|
... sender = forms.EmailField(help_text='A valid e-mail address, please.')
|
||||||
|
... cc_myself = forms.BooleanField()
|
||||||
|
>>> f = HelpTextContactForm(auto_id=False)
|
||||||
|
>>> print f.as_table()
|
||||||
|
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr>
|
||||||
|
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
|
||||||
|
<tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid e-mail address, please.</td></tr>
|
||||||
|
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</li>
|
||||||
|
<li>Message: <input type="text" name="message" /></li>
|
||||||
|
<li>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</li>
|
||||||
|
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
|
||||||
|
>>> print f.as_p()
|
||||||
|
<p>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</p>
|
||||||
|
<p>Message: <input type="text" name="message" /></p>
|
||||||
|
<p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p>
|
||||||
|
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
|
||||||
|
|
||||||
|
Dynamic initial values
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The ``initial`` argument to ``Field`` (explained above) lets you hard-code the
|
||||||
|
initial value for a ``Field`` -- but what if you want to declare the initial
|
||||||
|
value at runtime? For example, you might want to fill in a ``username`` field
|
||||||
|
with the username of the current session.
|
||||||
|
|
||||||
|
To accomplish this, use the ``initial`` argument to a ``Form``. This argument,
|
||||||
|
if given, should be a dictionary mapping field names to initial values. Only
|
||||||
|
include the fields for which you're specifying an initial value; it's not
|
||||||
|
necessary to include every field in your form. For example::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField()
|
||||||
|
... url = forms.URLField()
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> f = CommentForm(initial={'name': 'your username'}, auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="your username" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
>>> f = CommentForm(initial={'name': 'another username'}, auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="another username" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
|
Just like the ``initial`` parameter to ``Field``, these values are only
|
||||||
|
displayed for unbound forms, and they're not used as fallback values if a
|
||||||
|
particular value isn't provided.
|
||||||
|
|
||||||
|
Finally, note that if a ``Field`` defines ``initial`` *and* you include
|
||||||
|
``initial`` when instantiating the ``Form``, then the latter ``initial`` will
|
||||||
|
have precedence. In this example, ``initial`` is provided both at the field
|
||||||
|
level and at the form instance level, and the latter gets precedence::
|
||||||
|
|
||||||
|
>>> class CommentForm(forms.Form):
|
||||||
|
... name = forms.CharField(initial='class')
|
||||||
|
... url = forms.URLField()
|
||||||
|
... comment = forms.CharField()
|
||||||
|
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Name:</th><td><input type="text" name="name" value="instance" /></td></tr>
|
||||||
|
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||||
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
More coming soon
|
More coming soon
|
||||||
================
|
================
|
||||||
|
|
||||||
@ -333,8 +829,5 @@ what's possible.
|
|||||||
If you're really itching to learn and use this library, please be patient.
|
If you're really itching to learn and use this library, please be patient.
|
||||||
We're working hard on finishing both the code and documentation.
|
We're working hard on finishing both the code and documentation.
|
||||||
|
|
||||||
Using forms with templates
|
Widgets
|
||||||
==========================
|
=======
|
||||||
|
|
||||||
Using forms in views
|
|
||||||
====================
|
|
||||||
|
@ -24,7 +24,7 @@ and Django's ``HttpResponse`` objects are file-like objects.
|
|||||||
|
|
||||||
For more information on the CSV library, see the `CSV library docs`_.
|
For more information on the CSV library, see the `CSV library docs`_.
|
||||||
|
|
||||||
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/
|
.. _Request and response objects: ../request_response/
|
||||||
.. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html
|
.. _CSV library docs: http://www.python.org/doc/current/lib/module-csv.html
|
||||||
|
|
||||||
Here's an example::
|
Here's an example::
|
||||||
@ -115,5 +115,5 @@ a line of CSV for each row. It uses the `addslashes template filter`_ to ensure
|
|||||||
there aren't any problems with quotes. If you can be certain your data doesn't
|
there aren't any problems with quotes. If you can be certain your data doesn't
|
||||||
have single or double quotes in it, you can remove the ``addslashes`` filters.
|
have single or double quotes in it, you can remove the ``addslashes`` filters.
|
||||||
|
|
||||||
.. _Django template system: http://www.djangoproject.com/documentation/templates/
|
.. _Django template system: ../templates/
|
||||||
.. _addslashes template filter: http://www.djangoproject.com/documentation/templates/#addslashes
|
.. _addslashes template filter: ../templates/#addslashes
|
||||||
|
@ -43,7 +43,7 @@ objects.
|
|||||||
For more information on ``HttpResponse`` objects, see
|
For more information on ``HttpResponse`` objects, see
|
||||||
`Request and response objects`_.
|
`Request and response objects`_.
|
||||||
|
|
||||||
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/
|
.. _Request and response objects: ../request_response/
|
||||||
|
|
||||||
Here's a "Hello World" example::
|
Here's a "Hello World" example::
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ understand how Django works, but this isn't intended to be a tutorial or
|
|||||||
reference. Please see our more-detailed Django documentation_ when you're ready
|
reference. Please see our more-detailed Django documentation_ when you're ready
|
||||||
to start a project.
|
to start a project.
|
||||||
|
|
||||||
.. _documentation: http://www.djangoproject.com/documentation/
|
.. _documentation: ../
|
||||||
|
|
||||||
Design your model
|
Design your model
|
||||||
=================
|
=================
|
||||||
|
@ -15,8 +15,8 @@ To install the redirects app, follow these steps:
|
|||||||
to your MIDDLEWARE_CLASSES_ setting.
|
to your MIDDLEWARE_CLASSES_ setting.
|
||||||
3. Run the command ``manage.py syncdb``.
|
3. Run the command ``manage.py syncdb``.
|
||||||
|
|
||||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||||
.. _MIDDLEWARE_CLASSES: http://www.djangoproject.com/documentation/settings/#middleware-classes
|
.. _MIDDLEWARE_CLASSES: ../settings/#middleware-classes
|
||||||
|
|
||||||
How it works
|
How it works
|
||||||
============
|
============
|
||||||
@ -46,8 +46,8 @@ resort.
|
|||||||
|
|
||||||
For more on middleware, read the `middleware docs`_.
|
For more on middleware, read the `middleware docs`_.
|
||||||
|
|
||||||
.. _SITE_ID: http://www.djangoproject.com/documentation/settings/#site-id
|
.. _SITE_ID: ../settings/#site-id
|
||||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware docs: ../middleware/
|
||||||
|
|
||||||
How to add, change and delete redirects
|
How to add, change and delete redirects
|
||||||
=======================================
|
=======================================
|
||||||
@ -66,6 +66,6 @@ Redirects are represented by a standard `Django model`_, which lives in
|
|||||||
`django/contrib/redirects/models.py`_. You can access redirect
|
`django/contrib/redirects/models.py`_. You can access redirect
|
||||||
objects via the `Django database API`_.
|
objects via the `Django database API`_.
|
||||||
|
|
||||||
.. _Django model: http://www.djangoproject.com/documentation/model_api/
|
.. _Django model: ../model_api/
|
||||||
.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
|
.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
|
||||||
.. _Django database API: http://www.djangoproject.com/documentation/db_api/
|
.. _Django database API: ../db_api/
|
||||||
|
@ -117,14 +117,14 @@ All attributes except ``session`` should be considered read-only.
|
|||||||
``AuthenticationMiddleware`` activated. For more, see
|
``AuthenticationMiddleware`` activated. For more, see
|
||||||
`Authentication in Web requests`_.
|
`Authentication in Web requests`_.
|
||||||
|
|
||||||
.. _Authentication in Web requests: http://www.djangoproject.com/documentation/authentication/#authentication-in-web-requests
|
.. _Authentication in Web requests: ../authentication/#authentication-in-web-requests
|
||||||
|
|
||||||
``session``
|
``session``
|
||||||
A readable-and-writable, dictionary-like object that represents the current
|
A readable-and-writable, dictionary-like object that represents the current
|
||||||
session. This is only available if your Django installation has session
|
session. This is only available if your Django installation has session
|
||||||
support activated. See the `session documentation`_ for full details.
|
support activated. See the `session documentation`_ for full details.
|
||||||
|
|
||||||
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
.. _`session documentation`: ../sessions/
|
||||||
|
|
||||||
``raw_post_data``
|
``raw_post_data``
|
||||||
The raw HTTP POST data. This is only useful for advanced processing. Use
|
The raw HTTP POST data. This is only useful for advanced processing. Use
|
||||||
|
@ -27,7 +27,7 @@ If you don't want to use sessions, you might as well remove the
|
|||||||
``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
|
``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
|
||||||
from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
|
from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
|
||||||
|
|
||||||
.. _middleware: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware: ../middleware/
|
||||||
|
|
||||||
Using sessions in views
|
Using sessions in views
|
||||||
=======================
|
=======================
|
||||||
@ -290,7 +290,7 @@ Whether to save the session data on every request. If this is ``False``
|
|||||||
(default), then the session data will only be saved if it has been modified --
|
(default), then the session data will only be saved if it has been modified --
|
||||||
that is, if any of its dictionary values have been assigned or deleted.
|
that is, if any of its dictionary values have been assigned or deleted.
|
||||||
|
|
||||||
.. _Django settings: http://www.djangoproject.com/documentation/settings/
|
.. _Django settings: ../settings/
|
||||||
|
|
||||||
Technical details
|
Technical details
|
||||||
=================
|
=================
|
||||||
|
@ -59,7 +59,7 @@ Use the ``--settings`` command-line argument to specify the settings manually::
|
|||||||
|
|
||||||
django-admin.py runserver --settings=mysite.settings
|
django-admin.py runserver --settings=mysite.settings
|
||||||
|
|
||||||
.. _django-admin.py: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin.py: ../django_admin/
|
||||||
|
|
||||||
On the server (mod_python)
|
On the server (mod_python)
|
||||||
--------------------------
|
--------------------------
|
||||||
@ -75,7 +75,7 @@ settings file to use. Do that with ``SetEnv``::
|
|||||||
|
|
||||||
Read the `Django mod_python documentation`_ for more information.
|
Read the `Django mod_python documentation`_ for more information.
|
||||||
|
|
||||||
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/
|
.. _Django mod_python documentation: ../modpython/
|
||||||
|
|
||||||
Default settings
|
Default settings
|
||||||
================
|
================
|
||||||
@ -102,7 +102,7 @@ between the current settings file and Django's default settings.
|
|||||||
|
|
||||||
For more, see the `diffsettings documentation`_.
|
For more, see the `diffsettings documentation`_.
|
||||||
|
|
||||||
.. _diffsettings documentation: http://www.djangoproject.com/documentation/django_admin/#diffsettings
|
.. _diffsettings documentation: ../django_admin/#diffsettings
|
||||||
|
|
||||||
Using settings in Python code
|
Using settings in Python code
|
||||||
=============================
|
=============================
|
||||||
@ -157,13 +157,13 @@ ABSOLUTE_URL_OVERRIDES
|
|||||||
|
|
||||||
Default: ``{}`` (Empty dictionary)
|
Default: ``{}`` (Empty dictionary)
|
||||||
|
|
||||||
A dictionary mapping ``"app_label.module_name"`` strings to functions that take
|
A dictionary mapping ``"app_label.model_name"`` strings to functions that take
|
||||||
a model object and return its URL. This is a way of overriding
|
a model object and return its URL. This is a way of overriding
|
||||||
``get_absolute_url()`` methods on a per-installation basis. Example::
|
``get_absolute_url()`` methods on a per-installation basis. Example::
|
||||||
|
|
||||||
ABSOLUTE_URL_OVERRIDES = {
|
ABSOLUTE_URL_OVERRIDES = {
|
||||||
'blogs.blogs': lambda o: "/blogs/%s/" % o.slug,
|
'blogs.Weblog': lambda o: "/blogs/%s/" % o.slug,
|
||||||
'news.stories': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
|
'news.Story': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
|
||||||
}
|
}
|
||||||
|
|
||||||
ADMIN_FOR
|
ADMIN_FOR
|
||||||
@ -306,7 +306,7 @@ pages -- and, possibly, by other parts of the system. See
|
|||||||
|
|
||||||
See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
||||||
|
|
||||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
.. _allowed date format strings: ../templates/#now
|
||||||
|
|
||||||
DATETIME_FORMAT
|
DATETIME_FORMAT
|
||||||
---------------
|
---------------
|
||||||
@ -319,7 +319,7 @@ pages -- and, possibly, by other parts of the system. See
|
|||||||
|
|
||||||
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
|
||||||
|
|
||||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
.. _allowed date format strings: ../templates/#now
|
||||||
|
|
||||||
DEBUG
|
DEBUG
|
||||||
-----
|
-----
|
||||||
@ -433,7 +433,7 @@ A tuple of strings designating all applications that are enabled in this Django
|
|||||||
installation. Each string should be a full Python path to a Python package that
|
installation. Each string should be a full Python path to a Python package that
|
||||||
contains a Django application, as created by `django-admin.py startapp`_.
|
contains a Django application, as created by `django-admin.py startapp`_.
|
||||||
|
|
||||||
.. _django-admin.py startapp: http://www.djangoproject.com/documentation/django_admin/#startapp-appname
|
.. _django-admin.py startapp: ../django_admin/#startapp-appname
|
||||||
|
|
||||||
INTERNAL_IPS
|
INTERNAL_IPS
|
||||||
------------
|
------------
|
||||||
@ -464,7 +464,7 @@ A string representing the language code for this installation. This should be
|
|||||||
in standard language format. For example, U.S. English is ``"en-us"``. See the
|
in standard language format. For example, U.S. English is ``"en-us"``. See the
|
||||||
`internationalization docs`_.
|
`internationalization docs`_.
|
||||||
|
|
||||||
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/
|
.. _internationalization docs: ../i18n/
|
||||||
|
|
||||||
LANGUAGES
|
LANGUAGES
|
||||||
---------
|
---------
|
||||||
@ -557,6 +557,11 @@ Default: ``''`` (Empty string)
|
|||||||
URL that handles the media served from ``MEDIA_ROOT``.
|
URL that handles the media served from ``MEDIA_ROOT``.
|
||||||
Example: ``"http://media.lawrence.com"``
|
Example: ``"http://media.lawrence.com"``
|
||||||
|
|
||||||
|
Note that this should have a trailing slash if it has a path component.
|
||||||
|
|
||||||
|
Good: ``"http://www.example.com/static/"``
|
||||||
|
Bad: ``"http://www.example.com/static"``
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES
|
MIDDLEWARE_CLASSES
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@ -612,7 +617,7 @@ Default: Not defined
|
|||||||
A string representing the full Python import path to your root URLconf. For example:
|
A string representing the full Python import path to your root URLconf. For example:
|
||||||
``"mydjangoapps.urls"``. See `How Django processes a request`_.
|
``"mydjangoapps.urls"``. See `How Django processes a request`_.
|
||||||
|
|
||||||
.. _How Django processes a request: http://www.djangoproject.com/documentation/url_dispatch/#how-django-processes-a-request
|
.. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request
|
||||||
|
|
||||||
SECRET_KEY
|
SECRET_KEY
|
||||||
----------
|
----------
|
||||||
@ -704,7 +709,7 @@ and a single database can manage content for multiple sites.
|
|||||||
|
|
||||||
See the `site framework docs`_.
|
See the `site framework docs`_.
|
||||||
|
|
||||||
.. _site framework docs: http://www.djangoproject.com/documentation/sites/
|
.. _site framework docs: ../sites/
|
||||||
|
|
||||||
TEMPLATE_CONTEXT_PROCESSORS
|
TEMPLATE_CONTEXT_PROCESSORS
|
||||||
---------------------------
|
---------------------------
|
||||||
@ -760,7 +765,7 @@ Default: ``''`` (Empty string)
|
|||||||
Output, as a string, that the template system should use for invalid (e.g.
|
Output, as a string, that the template system should use for invalid (e.g.
|
||||||
misspelled) variables. See `How invalid variables are handled`_.
|
misspelled) variables. See `How invalid variables are handled`_.
|
||||||
|
|
||||||
.. _How invalid variables are handled: http://www.djangoproject.com/documentation/templates_python/#how-invalid-variables-are-handled
|
.. _How invalid variables are handled: ../templates_python/#how-invalid-variables-are-handled
|
||||||
|
|
||||||
TEST_RUNNER
|
TEST_RUNNER
|
||||||
-----------
|
-----------
|
||||||
@ -798,7 +803,7 @@ pages -- and, possibly, by other parts of the system. See
|
|||||||
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and
|
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and
|
||||||
MONTH_DAY_FORMAT.
|
MONTH_DAY_FORMAT.
|
||||||
|
|
||||||
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
|
.. _allowed date format strings: ../templates/#now
|
||||||
|
|
||||||
TIME_ZONE
|
TIME_ZONE
|
||||||
---------
|
---------
|
||||||
@ -868,11 +873,11 @@ Different locales have different formats. For example, U.S. English would say
|
|||||||
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
|
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
|
||||||
TIME_FORMAT and MONTH_DAY_FORMAT.
|
TIME_FORMAT and MONTH_DAY_FORMAT.
|
||||||
|
|
||||||
.. _cache docs: http://www.djangoproject.com/documentation/cache/
|
.. _cache docs: ../cache/
|
||||||
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
|
.. _middleware docs: ../middleware/
|
||||||
.. _session docs: http://www.djangoproject.com/documentation/sessions/
|
.. _session docs: ../sessions/
|
||||||
.. _See available choices: http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
.. _See available choices: http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||||
.. _template documentation: http://www.djangoproject.com/documentation/templates_python/
|
.. _template documentation: ../templates_python/
|
||||||
|
|
||||||
Creating your own settings
|
Creating your own settings
|
||||||
==========================
|
==========================
|
||||||
|
@ -23,8 +23,8 @@ you express this information in Python code.
|
|||||||
It works much like Django's `syndication framework`_. To create a sitemap, just
|
It works much like Django's `syndication framework`_. To create a sitemap, just
|
||||||
write a ``Sitemap`` class and point to it in your URLconf_.
|
write a ``Sitemap`` class and point to it in your URLconf_.
|
||||||
|
|
||||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
.. _syndication framework: ../syndication/
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
@ -41,9 +41,9 @@ To install the sitemap app, follow these steps:
|
|||||||
reason it needs to go into ``INSTALLED_APPS`` is so that the
|
reason it needs to go into ``INSTALLED_APPS`` is so that the
|
||||||
``load_template_source`` template loader can find the default templates.)
|
``load_template_source`` template loader can find the default templates.)
|
||||||
|
|
||||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
.. _INSTALLED_APPS: ../settings/#installed-apps
|
||||||
.. _TEMPLATE_LOADERS: http://www.djangoproject.com/documentation/settings/#template-loaders
|
.. _TEMPLATE_LOADERS: ../settings/#template-loaders
|
||||||
.. _sites framework: http://www.djangoproject.com/documentation/sites/
|
.. _sites framework: ../sites/
|
||||||
|
|
||||||
Initialization
|
Initialization
|
||||||
==============
|
==============
|
||||||
@ -68,7 +68,7 @@ The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
|
|||||||
``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
|
``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
|
||||||
(e.g., ``BlogSitemap(some_var)``).
|
(e.g., ``BlogSitemap(some_var)``).
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Sitemap classes
|
Sitemap classes
|
||||||
===============
|
===============
|
||||||
@ -217,8 +217,8 @@ defined for the current ``SITE_ID`` (see the `sites documentation`_) and
|
|||||||
creates an entry in the sitemap. These entries include only the ``location``
|
creates an entry in the sitemap. These entries include only the ``location``
|
||||||
attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
|
attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
|
||||||
|
|
||||||
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
|
.. _flatpages: ../flatpages/
|
||||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
.. _sites documentation: ../sites/
|
||||||
|
|
||||||
``GenericSitemap``
|
``GenericSitemap``
|
||||||
------------------
|
------------------
|
||||||
@ -232,7 +232,7 @@ the ``lastmod`` attribute in the generated sitemap. You may also pass
|
|||||||
``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
|
``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
|
||||||
constructor to specify these attributes for all URLs.
|
constructor to specify these attributes for all URLs.
|
||||||
|
|
||||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
.. _generic views: ../generic_views/
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
@ -261,7 +261,7 @@ Here's an example of a URLconf_ using both::
|
|||||||
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
||||||
)
|
)
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Creating a sitemap index
|
Creating a sitemap index
|
||||||
========================
|
========================
|
||||||
|
@ -276,8 +276,8 @@ you want your admin site to have access to all objects (not just site-specific
|
|||||||
ones), put ``objects = models.Manager()`` in your model, before you define
|
ones), put ``objects = models.Manager()`` in your model, before you define
|
||||||
``CurrentSiteManager``.
|
``CurrentSiteManager``.
|
||||||
|
|
||||||
.. _manager: http://www.djangoproject.com/documentation/model_api/#managers
|
.. _manager: ../model_api/#managers
|
||||||
.. _manager documentation: http://www.djangoproject.com/documentation/model_api/#managers
|
.. _manager documentation: ../model_api/#managers
|
||||||
|
|
||||||
How Django uses the sites framework
|
How Django uses the sites framework
|
||||||
===================================
|
===================================
|
||||||
@ -316,7 +316,7 @@ Here's how Django uses the sites framework:
|
|||||||
* The shortcut view (``django.views.defaults.shortcut``) uses the domain of
|
* The shortcut view (``django.views.defaults.shortcut``) uses the domain of
|
||||||
the current ``Site`` object when calculating an object's URL.
|
the current ``Site`` object when calculating an object's URL.
|
||||||
|
|
||||||
.. _redirects framework: http://www.djangoproject.com/documentation/redirects/
|
.. _redirects framework: ../redirects/
|
||||||
.. _flatpages framework: http://www.djangoproject.com/documentation/flatpages/
|
.. _flatpages framework: ../flatpages/
|
||||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
.. _syndication framework: ../syndication/
|
||||||
.. _authentication framework: http://www.djangoproject.com/documentation/authentication/
|
.. _authentication framework: ../authentication/
|
||||||
|
@ -24,7 +24,7 @@ production setting. Use this only for development.
|
|||||||
For information on serving static files in an Apache production environment,
|
For information on serving static files in an Apache production environment,
|
||||||
see the `Django mod_python documentation`_.
|
see the `Django mod_python documentation`_.
|
||||||
|
|
||||||
.. _Django mod_python documentation: http://www.djangoproject.com/documentation/modpython/#serving-media-files
|
.. _Django mod_python documentation: ../modpython/#serving-media-files
|
||||||
|
|
||||||
How to do it
|
How to do it
|
||||||
============
|
============
|
||||||
@ -49,7 +49,7 @@ Examples:
|
|||||||
* The file ``/path/bar.jpg`` will not be accessible, because it doesn't
|
* The file ``/path/bar.jpg`` will not be accessible, because it doesn't
|
||||||
fall under the document root.
|
fall under the document root.
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Directory listings
|
Directory listings
|
||||||
==================
|
==================
|
||||||
@ -122,4 +122,4 @@ associated with the ``django.views.static.serve`` view. If not
|
|||||||
Of course, the catch here is that you'll have to remember to set ``DEBUG=False``
|
Of course, the catch here is that you'll have to remember to set ``DEBUG=False``
|
||||||
in your production settings file. But you should be doing that anyway.
|
in your production settings file. But you should be doing that anyway.
|
||||||
|
|
||||||
.. _DEBUG setting: http://www.djangoproject.com/documentation/settings/#debug
|
.. _DEBUG setting: ../settings/#debug
|
||||||
|
@ -26,7 +26,7 @@ to determine which feed to output.
|
|||||||
|
|
||||||
To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
|
To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
|
|
||||||
Initialization
|
Initialization
|
||||||
--------------
|
--------------
|
||||||
@ -72,8 +72,8 @@ The above example registers two feeds:
|
|||||||
|
|
||||||
Once that's set up, you just need to define the ``Feed`` classes themselves.
|
Once that's set up, you just need to define the ``Feed`` classes themselves.
|
||||||
|
|
||||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf: ../url_dispatch/
|
||||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
.. _settings file: ../settings/
|
||||||
|
|
||||||
Feed classes
|
Feed classes
|
||||||
------------
|
------------
|
||||||
@ -156,8 +156,8 @@ put into those elements.
|
|||||||
{{ obj.description }}
|
{{ obj.description }}
|
||||||
|
|
||||||
.. _chicagocrime.org: http://www.chicagocrime.org/
|
.. _chicagocrime.org: http://www.chicagocrime.org/
|
||||||
.. _object-relational mapper: http://www.djangoproject.com/documentation/db_api/
|
.. _object-relational mapper: ../db_api/
|
||||||
.. _Django templates: http://www.djangoproject.com/documentation/templates/
|
.. _Django templates: ../templates/
|
||||||
|
|
||||||
A complex example
|
A complex example
|
||||||
-----------------
|
-----------------
|
||||||
@ -277,7 +277,7 @@ Feeds created by the syndication framework automatically include the
|
|||||||
appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
|
appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
|
||||||
comes directly from your `LANGUAGE_CODE setting`_.
|
comes directly from your `LANGUAGE_CODE setting`_.
|
||||||
|
|
||||||
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code
|
.. _LANGUAGE_CODE setting: ../settings/#language-code
|
||||||
|
|
||||||
URLs
|
URLs
|
||||||
----
|
----
|
||||||
@ -292,7 +292,7 @@ Atom feeds require a ``<link rel="self">`` that defines the feed's current
|
|||||||
location. The syndication framework populates this automatically, using the
|
location. The syndication framework populates this automatically, using the
|
||||||
domain of the current site according to the SITE_ID setting.
|
domain of the current site according to the SITE_ID setting.
|
||||||
|
|
||||||
.. _SITE_ID setting: http://www.djangoproject.com/documentation/settings/#site-id
|
.. _SITE_ID setting: ../settings/#site-id
|
||||||
|
|
||||||
Publishing Atom and RSS feeds in tandem
|
Publishing Atom and RSS feeds in tandem
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
@ -792,7 +792,7 @@ Note that if you use ``{% ssi %}``, you'll need to define
|
|||||||
|
|
||||||
See also: ``{% include %}``.
|
See also: ``{% include %}``.
|
||||||
|
|
||||||
.. _ALLOWED_INCLUDE_ROOTS: http://www.djangoproject.com/documentation/settings/#allowed-include-roots
|
.. _ALLOWED_INCLUDE_ROOTS: ../settings/#allowed-include-roots
|
||||||
|
|
||||||
templatetag
|
templatetag
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
@ -1218,7 +1218,7 @@ django.contrib.humanize
|
|||||||
A set of Django template filters useful for adding a "human touch" to data. See
|
A set of Django template filters useful for adding a "human touch" to data. See
|
||||||
the `humanize documentation`_.
|
the `humanize documentation`_.
|
||||||
|
|
||||||
.. _humanize documentation: http://www.djangoproject.com/documentation/add_ons/#humanize
|
.. _humanize documentation: ../add_ons/#humanize
|
||||||
|
|
||||||
django.contrib.markup
|
django.contrib.markup
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -11,7 +11,7 @@ If you're looking to use the Django template system as part of another
|
|||||||
application -- i.e., without the rest of the framework -- make sure to read
|
application -- i.e., without the rest of the framework -- make sure to read
|
||||||
the `configuration`_ section later in this document.
|
the `configuration`_ section later in this document.
|
||||||
|
|
||||||
.. _`The Django template language: For template authors`: http://www.djangoproject.com/documentation/templates/
|
.. _`The Django template language: For template authors`: ../templates/
|
||||||
|
|
||||||
Basics
|
Basics
|
||||||
======
|
======
|
||||||
@ -327,8 +327,8 @@ Note::
|
|||||||
|
|
||||||
Here's what each of the default processors does:
|
Here's what each of the default processors does:
|
||||||
|
|
||||||
.. _HttpRequest object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
.. _HttpRequest object: ../request_response/#httprequest-objects
|
||||||
.. _TEMPLATE_CONTEXT_PROCESSORS setting: http://www.djangoproject.com/documentation/settings/#template-context-processors
|
.. _TEMPLATE_CONTEXT_PROCESSORS setting: ../settings/#template-context-processors
|
||||||
|
|
||||||
django.core.context_processors.auth
|
django.core.context_processors.auth
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -353,9 +353,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
|||||||
permissions that the currently logged-in user has. See the `permissions
|
permissions that the currently logged-in user has. See the `permissions
|
||||||
docs`_.
|
docs`_.
|
||||||
|
|
||||||
.. _user authentication docs: http://www.djangoproject.com/documentation/authentication/#users
|
.. _user authentication docs: ../authentication/#users
|
||||||
.. _message docs: http://www.djangoproject.com/documentation/authentication/#messages
|
.. _message docs: ../authentication/#messages
|
||||||
.. _permissions docs: http://www.djangoproject.com/documentation/authentication/#permissions
|
.. _permissions docs: ../authentication/#permissions
|
||||||
|
|
||||||
django.core.context_processors.debug
|
django.core.context_processors.debug
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -383,9 +383,9 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
|||||||
|
|
||||||
See the `internationalization docs`_ for more.
|
See the `internationalization docs`_ for more.
|
||||||
|
|
||||||
.. _LANGUAGES setting: http://www.djangoproject.com/documentation/settings/#languages
|
.. _LANGUAGES setting: ../settings/#languages
|
||||||
.. _LANGUAGE_CODE setting: http://www.djangoproject.com/documentation/settings/#language-code
|
.. _LANGUAGE_CODE setting: ../settings/#language-code
|
||||||
.. _internationalization docs: http://www.djangoproject.com/documentation/i18n/
|
.. _internationalization docs: ../i18n/
|
||||||
|
|
||||||
django.core.context_processors.request
|
django.core.context_processors.request
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -944,7 +944,7 @@ The ``takes_context`` parameter defaults to ``False``. When it's set to *True*,
|
|||||||
the tag is passed the context object, as in this example. That's the only
|
the tag is passed the context object, as in this example. That's the only
|
||||||
difference between this case and the previous ``inclusion_tag`` example.
|
difference between this case and the previous ``inclusion_tag`` example.
|
||||||
|
|
||||||
.. _tutorials: http://www.djangoproject.com/documentation/tutorial1/#creating-models
|
.. _tutorials: ../tutorial1/#creating-models
|
||||||
|
|
||||||
Setting a variable in the context
|
Setting a variable in the context
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -1115,5 +1115,5 @@ settings you wish to specify. You might want to consider setting at least
|
|||||||
`settings documentation`_, and any setting starting with *TEMPLATE_*
|
`settings documentation`_, and any setting starting with *TEMPLATE_*
|
||||||
is of obvious interest.
|
is of obvious interest.
|
||||||
|
|
||||||
.. _settings file: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
|
.. _settings file: ../settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||||
.. _settings documentation: http://www.djangoproject.com/documentation/settings/
|
.. _settings documentation: ../settings/
|
||||||
|
@ -13,7 +13,7 @@ changed unexpectedly as a result of the refactor.
|
|||||||
Testing a web application is a complex task, as there are many
|
Testing a web application is a complex task, as there are many
|
||||||
components of a web application that must be validated and tested. To
|
components of a web application that must be validated and tested. To
|
||||||
help you test your application, Django provides a test execution
|
help you test your application, Django provides a test execution
|
||||||
framework, and range of utilities that can be used to stimulate and
|
framework, and range of utilities that can be used to simulate and
|
||||||
inspect various facets of a web application.
|
inspect various facets of a web application.
|
||||||
|
|
||||||
This testing framework is currently under development, and may change
|
This testing framework is currently under development, and may change
|
||||||
@ -220,7 +220,7 @@ can be invoked on the ``Client`` instance.
|
|||||||
|
|
||||||
will result in the evaluation of a GET request equivalent to::
|
will result in the evaluation of a GET request equivalent to::
|
||||||
|
|
||||||
http://yoursite.com/customers/details/?name='fred'&age=7
|
http://yoursite.com/customers/details/?name=fred&age=7
|
||||||
|
|
||||||
``post(path, data={})``
|
``post(path, data={})``
|
||||||
Make a POST request on the provided ``path``. The key-value pairs in the
|
Make a POST request on the provided ``path``. The key-value pairs in the
|
||||||
@ -244,11 +244,11 @@ can be invoked on the ``Client`` instance.
|
|||||||
|
|
||||||
``login(path, username, password)``
|
``login(path, username, password)``
|
||||||
In a production site, it is likely that some views will be protected with
|
In a production site, it is likely that some views will be protected with
|
||||||
the @login_required URL provided by ``django.contrib.auth``. Interacting
|
the @login_required decorator provided by ``django.contrib.auth``. Interacting
|
||||||
with a URL that has been login protected is a slightly complex operation,
|
with a URL that has been login protected is a slightly complex operation,
|
||||||
so the Test Client provides a simple URL to automate the login process. A
|
so the Test Client provides a simple method to automate the login process. A
|
||||||
call to ``login()`` stimulates the series of GET and POST calls required
|
call to ``login()`` stimulates the series of GET and POST calls required
|
||||||
to log a user into a @login_required protected URL.
|
to log a user into a @login_required protected view.
|
||||||
|
|
||||||
If login is possible, the final return value of ``login()`` is the response
|
If login is possible, the final return value of ``login()`` is the response
|
||||||
that is generated by issuing a GET request on the protected URL. If login
|
that is generated by issuing a GET request on the protected URL. If login
|
||||||
@ -415,7 +415,7 @@ arguments:
|
|||||||
tested. This is the same format returned by ``django.db.models.get_apps()``
|
tested. This is the same format returned by ``django.db.models.get_apps()``
|
||||||
|
|
||||||
Verbosity determines the amount of notification and debug information that
|
Verbosity determines the amount of notification and debug information that
|
||||||
will be printed to the console; '0' is no output, '1' is normal output,
|
will be printed to the console; `0` is no output, `1` is normal output,
|
||||||
and `2` is verbose output.
|
and `2` is verbose output.
|
||||||
|
|
||||||
Testing utilities
|
Testing utilities
|
||||||
|
@ -17,7 +17,7 @@ installed by running the Python interactive interpreter and typing
|
|||||||
``import django``. If that command runs successfully, with no errors, Django is
|
``import django``. If that command runs successfully, with no errors, Django is
|
||||||
installed.
|
installed.
|
||||||
|
|
||||||
.. _`Django installed`: http://www.djangoproject.com/documentation/install/
|
.. _`Django installed`: ../install/
|
||||||
|
|
||||||
Creating a project
|
Creating a project
|
||||||
==================
|
==================
|
||||||
@ -108,7 +108,7 @@ It worked!
|
|||||||
|
|
||||||
Full docs for the development server are at `django-admin documentation`_.
|
Full docs for the development server are at `django-admin documentation`_.
|
||||||
|
|
||||||
.. _django-admin documentation: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin documentation: ../django_admin/
|
||||||
|
|
||||||
Database setup
|
Database setup
|
||||||
--------------
|
--------------
|
||||||
@ -378,7 +378,7 @@ as you like, and it will only ever create the tables that don't exist.
|
|||||||
Read the `django-admin.py documentation`_ for full information on what the
|
Read the `django-admin.py documentation`_ for full information on what the
|
||||||
``manage.py`` utility can do.
|
``manage.py`` utility can do.
|
||||||
|
|
||||||
.. _django-admin.py documentation: http://www.djangoproject.com/documentation/django_admin/
|
.. _django-admin.py documentation: ../django_admin/
|
||||||
|
|
||||||
Playing with the API
|
Playing with the API
|
||||||
====================
|
====================
|
||||||
@ -555,5 +555,5 @@ For full details on the database API, see our `Database API reference`_.
|
|||||||
When you're comfortable with the API, read `part 2 of this tutorial`_ to get
|
When you're comfortable with the API, read `part 2 of this tutorial`_ to get
|
||||||
Django's automatic admin working.
|
Django's automatic admin working.
|
||||||
|
|
||||||
.. _Database API reference: http://www.djangoproject.com/documentation/db_api/
|
.. _Database API reference: ../db_api/
|
||||||
.. _part 2 of this tutorial: http://www.djangoproject.com/documentation/tutorial2/
|
.. _part 2 of this tutorial: ../tutorial2/
|
||||||
|
@ -5,7 +5,7 @@ Writing your first Django app, part 2
|
|||||||
This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll
|
This tutorial begins where `Tutorial 1`_ left off. We're continuing the Web-poll
|
||||||
application and will focus on Django's automatically-generated admin site.
|
application and will focus on Django's automatically-generated admin site.
|
||||||
|
|
||||||
.. _Tutorial 1: http://www.djangoproject.com/documentation/tutorial1/
|
.. _Tutorial 1: ../tutorial1/
|
||||||
|
|
||||||
.. admonition:: Philosophy
|
.. admonition:: Philosophy
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ tutorial, remember?) You should see the Django admin index page:
|
|||||||
By default, you should see two types of editable content: groups and users.
|
By default, you should see two types of editable content: groups and users.
|
||||||
These are core features Django ships with by default.
|
These are core features Django ships with by default.
|
||||||
|
|
||||||
.. _"I can't log in" questions: http://www.djangoproject.com/documentation/faq/#the-admin-site
|
.. _"I can't log in" questions: ../faq/#the-admin-site
|
||||||
|
|
||||||
Make the poll app modifiable in the admin
|
Make the poll app modifiable in the admin
|
||||||
=========================================
|
=========================================
|
||||||
@ -402,7 +402,7 @@ Django automatically looks for a ``templates/`` subdirectory within each app
|
|||||||
package, for use as a fallback. See the `loader types documentation`_ for full
|
package, for use as a fallback. See the `loader types documentation`_ for full
|
||||||
information.
|
information.
|
||||||
|
|
||||||
.. _loader types documentation: http://www.djangoproject.com/documentation/templates_python/#loader-types
|
.. _loader types documentation: ../templates_python/#loader-types
|
||||||
|
|
||||||
Customize the admin index page
|
Customize the admin index page
|
||||||
==============================
|
==============================
|
||||||
@ -433,5 +433,5 @@ general, see the `Django admin CSS guide`_.
|
|||||||
When you're comfortable with the admin site, read `part 3 of this tutorial`_ to
|
When you're comfortable with the admin site, read `part 3 of this tutorial`_ to
|
||||||
start working on public poll views.
|
start working on public poll views.
|
||||||
|
|
||||||
.. _Django admin CSS guide: http://www.djangoproject.com/documentation/admin_css/
|
.. _Django admin CSS guide: ../admin_css/
|
||||||
.. _part 3 of this tutorial: http://www.djangoproject.com/documentation/tutorial3/
|
.. _part 3 of this tutorial: ../tutorial3/
|
||||||
|
@ -5,7 +5,7 @@ Writing your first Django app, part 3
|
|||||||
This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
|
This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
|
||||||
application and will focus on creating the public interface -- "views."
|
application and will focus on creating the public interface -- "views."
|
||||||
|
|
||||||
.. _Tutorial 2: http://www.djangoproject.com/documentation/tutorial2/
|
.. _Tutorial 2: ../tutorial2/
|
||||||
|
|
||||||
Philosophy
|
Philosophy
|
||||||
==========
|
==========
|
||||||
@ -117,8 +117,8 @@ time the URLconf module is loaded. They're super fast.
|
|||||||
|
|
||||||
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
|
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
|
||||||
.. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
|
.. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
|
||||||
.. _request and response documentation: http://www.djangoproject.com/documentation/request_response/
|
.. _request and response documentation: ../request_response/
|
||||||
.. _URLconf documentation: http://www.djangoproject.com/documentation/url_dispatch/
|
.. _URLconf documentation: ../url_dispatch/
|
||||||
|
|
||||||
Write your first view
|
Write your first view
|
||||||
=====================
|
=====================
|
||||||
@ -260,8 +260,7 @@ provides a shortcut. Here's the full ``index()`` view, rewritten::
|
|||||||
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
|
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
|
||||||
return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
|
return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
|
||||||
|
|
||||||
Note that we no longer need to import ``loader``, ``Context`` or
|
Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
|
||||||
``HttpResponse``.
|
|
||||||
|
|
||||||
The ``render_to_response()`` function takes a template name as its first
|
The ``render_to_response()`` function takes a template name as its first
|
||||||
argument and a dictionary as its optional second argument. It returns an
|
argument and a dictionary as its optional second argument. It returns an
|
||||||
@ -377,7 +376,7 @@ iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
|
|||||||
|
|
||||||
See the `template guide`_ for full details on how templates work.
|
See the `template guide`_ for full details on how templates work.
|
||||||
|
|
||||||
.. _template guide: http://www.djangoproject.com/documentation/templates/
|
.. _template guide: ../templates/
|
||||||
|
|
||||||
Simplifying the URLconfs
|
Simplifying the URLconfs
|
||||||
========================
|
========================
|
||||||
@ -464,4 +463,4 @@ All the poll app cares about is its relative URLs, not its absolute URLs.
|
|||||||
When you're comfortable with writing views, read `part 4 of this tutorial`_ to
|
When you're comfortable with writing views, read `part 4 of this tutorial`_ to
|
||||||
learn about simple form processing and generic views.
|
learn about simple form processing and generic views.
|
||||||
|
|
||||||
.. _part 4 of this tutorial: http://www.djangoproject.com/documentation/tutorial4/
|
.. _part 4 of this tutorial: ../tutorial4/
|
||||||
|
@ -120,7 +120,7 @@ Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a
|
|||||||
results page that gets updated each time you vote. If you submit the form
|
results page that gets updated each time you vote. If you submit the form
|
||||||
without having chosen a choice, you should see the error message.
|
without having chosen a choice, you should see the error message.
|
||||||
|
|
||||||
.. _request and response documentation: http://www.djangoproject.com/documentation/request_response/
|
.. _request and response documentation: ../request_response/
|
||||||
|
|
||||||
Use generic views: Less code is better
|
Use generic views: Less code is better
|
||||||
======================================
|
======================================
|
||||||
@ -226,7 +226,7 @@ Run the server, and use your new polling app based on generic views.
|
|||||||
|
|
||||||
For full details on generic views, see the `generic views documentation`_.
|
For full details on generic views, see the `generic views documentation`_.
|
||||||
|
|
||||||
.. _generic views documentation: http://www.djangoproject.com/documentation/generic_views/
|
.. _generic views documentation: ../generic_views/
|
||||||
|
|
||||||
Coming soon
|
Coming soon
|
||||||
===========
|
===========
|
||||||
@ -241,4 +241,4 @@ installments:
|
|||||||
* Advanced admin features: Permissions
|
* Advanced admin features: Permissions
|
||||||
* Advanced admin features: Custom JavaScript
|
* Advanced admin features: Custom JavaScript
|
||||||
|
|
||||||
.. _Tutorial 3: http://www.djangoproject.com/documentation/tutorial3/
|
.. _Tutorial 3: ../tutorial3/
|
||||||
|
@ -45,8 +45,8 @@ algorithm the system follows to determine which Python code to execute:
|
|||||||
`request object`_ as its first argument and any values captured in the
|
`request object`_ as its first argument and any values captured in the
|
||||||
regex as remaining arguments.
|
regex as remaining arguments.
|
||||||
|
|
||||||
.. _settings file: http://www.djangoproject.com/documentation/settings/
|
.. _settings file: ../settings/
|
||||||
.. _request object: http://www.djangoproject.com/documentation/request_response/#httprequest-objects
|
.. _request object: ../request_response/#httprequest-objects
|
||||||
|
|
||||||
Example
|
Example
|
||||||
=======
|
=======
|
||||||
@ -286,7 +286,7 @@ With this in mind, the above example can be written more concisely as::
|
|||||||
Note that you don't put a trailing dot (``"."``) in the prefix. Django puts
|
Note that you don't put a trailing dot (``"."``) in the prefix. Django puts
|
||||||
that in automatically.
|
that in automatically.
|
||||||
|
|
||||||
.. _Django overview: http://www.djangoproject.com/documentation/overview/
|
.. _Django overview: ../overview/
|
||||||
|
|
||||||
Multiple view prefixes
|
Multiple view prefixes
|
||||||
----------------------
|
----------------------
|
||||||
@ -387,13 +387,13 @@ In this example, for a request to ``/blog/2005/``, Django will call the
|
|||||||
This technique is used in `generic views`_ and in the `syndication framework`_
|
This technique is used in `generic views`_ and in the `syndication framework`_
|
||||||
to pass metadata and options to views.
|
to pass metadata and options to views.
|
||||||
|
|
||||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
.. _generic views: ../generic_views/
|
||||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
.. _syndication framework: ../syndication/
|
||||||
|
|
||||||
Passing extra options to ``include()``
|
Passing extra options to ``include()``
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
**New in the Django development version.**
|
**New in Django development version.**
|
||||||
|
|
||||||
Similarly, you can pass extra options to ``include()``. When you pass extra
|
Similarly, you can pass extra options to ``include()``. When you pass extra
|
||||||
options to ``include()``, *each* line in the included URLconf will be passed
|
options to ``include()``, *each* line in the included URLconf will be passed
|
||||||
@ -435,7 +435,7 @@ every view in the the included URLconf accepts the extra options you're passing.
|
|||||||
Passing callable objects instead of strings
|
Passing callable objects instead of strings
|
||||||
===========================================
|
===========================================
|
||||||
|
|
||||||
**New in the Django development version.**
|
**New in Django development version.**
|
||||||
|
|
||||||
Some developers find it more natural to pass the actual Python function object
|
Some developers find it more natural to pass the actual Python function object
|
||||||
rather than a string containing the path to its module. This alternative is
|
rather than a string containing the path to its module. This alternative is
|
||||||
|
@ -1,53 +1,105 @@
|
|||||||
"""
|
"""
|
||||||
17. Custom column names
|
17. Custom column/table names
|
||||||
|
|
||||||
If your database column name is different than your model attribute, use the
|
If your database column name is different than your model attribute, use the
|
||||||
``db_column`` parameter. Note that you'll use the field's name, not its column
|
``db_column`` parameter. Note that you'll use the field's name, not its column
|
||||||
name, in API usage.
|
name, in API usage.
|
||||||
|
|
||||||
|
If your database table name is different than your model name, use the
|
||||||
|
``db_table`` Meta attribute. This has no effect on the API used to
|
||||||
|
query the database.
|
||||||
|
|
||||||
|
If you need to use a table name for a many-to-many relationship that differs
|
||||||
|
from the default generated name, use the ``db_table`` parameter on the
|
||||||
|
ManyToMany field. This has no effect on the API for querying the database.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Person(models.Model):
|
class Author(models.Model):
|
||||||
first_name = models.CharField(maxlength=30, db_column='firstname')
|
first_name = models.CharField(maxlength=30, db_column='firstname')
|
||||||
last_name = models.CharField(maxlength=30, db_column='last')
|
last_name = models.CharField(maxlength=30, db_column='last')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s %s' % (self.first_name, self.last_name)
|
return '%s %s' % (self.first_name, self.last_name)
|
||||||
|
|
||||||
__test__ = {'API_TESTS':"""
|
class Meta:
|
||||||
# Create a Person.
|
db_table = 'my_author_table'
|
||||||
>>> p = Person(first_name='John', last_name='Smith')
|
ordering = ('last_name','first_name')
|
||||||
>>> p.save()
|
|
||||||
|
|
||||||
>>> p.id
|
class Article(models.Model):
|
||||||
|
headline = models.CharField(maxlength=100)
|
||||||
|
authors = models.ManyToManyField(Author, db_table='my_m2m_table')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.headline
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('headline',)
|
||||||
|
|
||||||
|
__test__ = {'API_TESTS':"""
|
||||||
|
# Create a Author.
|
||||||
|
>>> a = Author(first_name='John', last_name='Smith')
|
||||||
|
>>> a.save()
|
||||||
|
|
||||||
|
>>> a.id
|
||||||
1
|
1
|
||||||
|
|
||||||
>>> Person.objects.all()
|
# Create another author
|
||||||
[<Person: John Smith>]
|
>>> a2 = Author(first_name='Peter', last_name='Jones')
|
||||||
|
>>> a2.save()
|
||||||
|
|
||||||
>>> Person.objects.filter(first_name__exact='John')
|
# Create an article
|
||||||
[<Person: John Smith>]
|
>>> art = Article(headline='Django lets you build web apps easily')
|
||||||
|
>>> art.save()
|
||||||
|
>>> art.authors = [a, a2]
|
||||||
|
|
||||||
>>> Person.objects.get(first_name__exact='John')
|
# Although the table and column names on Author have been set to
|
||||||
<Person: John Smith>
|
# custom values, nothing about using the Author model has changed...
|
||||||
|
|
||||||
>>> Person.objects.filter(firstname__exact='John')
|
# Query the available authors
|
||||||
|
>>> Author.objects.all()
|
||||||
|
[<Author: Peter Jones>, <Author: John Smith>]
|
||||||
|
|
||||||
|
>>> Author.objects.filter(first_name__exact='John')
|
||||||
|
[<Author: John Smith>]
|
||||||
|
|
||||||
|
>>> Author.objects.get(first_name__exact='John')
|
||||||
|
<Author: John Smith>
|
||||||
|
|
||||||
|
>>> Author.objects.filter(firstname__exact='John')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'firstname' into field
|
TypeError: Cannot resolve keyword 'firstname' into field
|
||||||
|
|
||||||
>>> p = Person.objects.get(last_name__exact='Smith')
|
>>> a = Author.objects.get(last_name__exact='Smith')
|
||||||
>>> p.first_name
|
>>> a.first_name
|
||||||
'John'
|
'John'
|
||||||
>>> p.last_name
|
>>> a.last_name
|
||||||
'Smith'
|
'Smith'
|
||||||
>>> p.firstname
|
>>> a.firstname
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'Person' object has no attribute 'firstname'
|
AttributeError: 'Author' object has no attribute 'firstname'
|
||||||
>>> p.last
|
>>> a.last
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'Person' object has no attribute 'last'
|
AttributeError: 'Author' object has no attribute 'last'
|
||||||
|
|
||||||
|
# Although the Article table uses a custom m2m table,
|
||||||
|
# nothing about using the m2m relationship has changed...
|
||||||
|
|
||||||
|
# Get all the authors for an article
|
||||||
|
>>> art.authors.all()
|
||||||
|
[<Author: Peter Jones>, <Author: John Smith>]
|
||||||
|
|
||||||
|
# Get the articles for an author
|
||||||
|
>>> a.article_set.all()
|
||||||
|
[<Article: Django lets you build web apps easily>]
|
||||||
|
|
||||||
|
# Query the authors across the m2m relation
|
||||||
|
>>> art.authors.filter(last_name='Jones')
|
||||||
|
[<Author: Peter Jones>]
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -65,14 +65,14 @@ __test__ = {'API_TESTS':"""
|
|||||||
|
|
||||||
# Objects with declared GenericRelations can be tagged directly -- the API
|
# Objects with declared GenericRelations can be tagged directly -- the API
|
||||||
# mimics the many-to-many API.
|
# mimics the many-to-many API.
|
||||||
>>> lion.tags.create(tag="yellow")
|
|
||||||
<TaggedItem: yellow>
|
|
||||||
>>> lion.tags.create(tag="hairy")
|
|
||||||
<TaggedItem: hairy>
|
|
||||||
>>> bacon.tags.create(tag="fatty")
|
>>> bacon.tags.create(tag="fatty")
|
||||||
<TaggedItem: fatty>
|
<TaggedItem: fatty>
|
||||||
>>> bacon.tags.create(tag="salty")
|
>>> bacon.tags.create(tag="salty")
|
||||||
<TaggedItem: salty>
|
<TaggedItem: salty>
|
||||||
|
>>> lion.tags.create(tag="yellow")
|
||||||
|
<TaggedItem: yellow>
|
||||||
|
>>> lion.tags.create(tag="hairy")
|
||||||
|
<TaggedItem: hairy>
|
||||||
|
|
||||||
>>> lion.tags.all()
|
>>> lion.tags.all()
|
||||||
[<TaggedItem: hairy>, <TaggedItem: yellow>]
|
[<TaggedItem: hairy>, <TaggedItem: yellow>]
|
||||||
@ -105,4 +105,30 @@ __test__ = {'API_TESTS':"""
|
|||||||
[<TaggedItem: shiny>]
|
[<TaggedItem: shiny>]
|
||||||
>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
|
>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
|
||||||
[<TaggedItem: clearish>]
|
[<TaggedItem: clearish>]
|
||||||
|
|
||||||
|
# If you delete an object with an explicit Generic relation, the related
|
||||||
|
# objects are deleted when the source object is deleted.
|
||||||
|
# Original list of tags:
|
||||||
|
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
|
||||||
|
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('hairy', <ContentType: animal>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2), ('yellow', <ContentType: animal>, 1)]
|
||||||
|
|
||||||
|
>>> lion.delete()
|
||||||
|
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
|
||||||
|
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
|
||||||
|
|
||||||
|
# If Generic Relation is not explicitly defined, any related objects
|
||||||
|
# remain after deletion of the source object.
|
||||||
|
>>> quartz.delete()
|
||||||
|
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
|
||||||
|
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
|
||||||
|
|
||||||
|
# If you delete a tag, the objects using the tag are unaffected
|
||||||
|
# (other than losing a tag)
|
||||||
|
>>> tag = TaggedItem.objects.get(id=1)
|
||||||
|
>>> tag.delete()
|
||||||
|
>>> bacon.tags.all()
|
||||||
|
[<TaggedItem: salty>]
|
||||||
|
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
|
||||||
|
[('clearish', <ContentType: mineral>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -191,4 +191,19 @@ DoesNotExist: Article matching query does not exist.
|
|||||||
>>> Article.objects.filter(headline__contains='\\')
|
>>> Article.objects.filter(headline__contains='\\')
|
||||||
[<Article: Article with \ backslash>]
|
[<Article: Article with \ backslash>]
|
||||||
|
|
||||||
|
# none() returns an EmptyQuerySet that behaves like any other QuerySet object
|
||||||
|
>>> Article.objects.none()
|
||||||
|
[]
|
||||||
|
>>> Article.objects.none().filter(headline__startswith='Article')
|
||||||
|
[]
|
||||||
|
>>> Article.objects.none().count()
|
||||||
|
0
|
||||||
|
|
||||||
|
# using __in with an empty list should return an empty query set
|
||||||
|
>>> Article.objects.filter(id__in=[])
|
||||||
|
[]
|
||||||
|
|
||||||
|
>>> Article.objects.exclude(id__in=[])
|
||||||
|
[<Article: Article with \ backslash>, <Article: Article% with percent sign>, <Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -203,7 +203,19 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> p2.article_set.all()
|
>>> p2.article_set.all()
|
||||||
[<Article: Oxygen-free diet works wonders>]
|
[<Article: Oxygen-free diet works wonders>]
|
||||||
|
|
||||||
# Recreate the article and Publication we just deleted.
|
# Relation sets can also be set using primary key values
|
||||||
|
>>> p2.article_set = [a4.id, a5.id]
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]
|
||||||
|
>>> a4.publications.all()
|
||||||
|
[<Publication: Science News>]
|
||||||
|
>>> a4.publications = [p3.id]
|
||||||
|
>>> p2.article_set.all()
|
||||||
|
[<Article: Oxygen-free diet works wonders>]
|
||||||
|
>>> a4.publications.all()
|
||||||
|
[<Publication: Science Weekly>]
|
||||||
|
|
||||||
|
# Recreate the article and Publication we have deleted.
|
||||||
>>> p1 = Publication(id=None, title='The Python Journal')
|
>>> p1 = Publication(id=None, title='The Python Journal')
|
||||||
>>> p1.save()
|
>>> p1.save()
|
||||||
>>> a2 = Article(id=None, headline='NASA uses Python')
|
>>> a2 = Article(id=None, headline='NASA uses Python')
|
||||||
|
@ -6,17 +6,20 @@ model instance.
|
|||||||
|
|
||||||
The function django.newforms.form_for_model() takes a model class and returns
|
The function django.newforms.form_for_model() takes a model class and returns
|
||||||
a Form that is tied to the model. This Form works just like any other Form,
|
a Form that is tied to the model. This Form works just like any other Form,
|
||||||
with one additional method: create(). The create() method creates an instance
|
with one additional method: save(). The save() method creates an instance
|
||||||
of the model and returns that newly created instance. It saves the instance to
|
of the model and returns that newly created instance. It saves the instance to
|
||||||
the database if create(save=True), which is default. If you pass
|
the database if save(commit=True), which is default. If you pass
|
||||||
create(save=False), then you'll get the object without saving it.
|
commit=False, then you'll get the object without committing the changes to the
|
||||||
|
database.
|
||||||
|
|
||||||
The function django.newforms.form_for_instance() takes a model instance and
|
The function django.newforms.form_for_instance() takes a model instance and
|
||||||
returns a Form that is tied to the instance. This form works just like any
|
returns a Form that is tied to the instance. This form works just like any
|
||||||
other Form, with one additional method: apply_changes(). The apply_changes()
|
other Form, with one additional method: save(). The save()
|
||||||
method updates the model instance. It saves the changes to the database if
|
method updates the model instance. It also takes a commit=True parameter.
|
||||||
apply_changes(save=True), which is default. If you pass save=False, then you'll
|
|
||||||
get the object without saving it.
|
The function django.newforms.save_instance() takes a bound form instance and a
|
||||||
|
model instance and saves the form's clean_data into the instance. It also takes
|
||||||
|
a commit=True parameter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@ -29,7 +32,7 @@ class Category(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class Writer(models.Model):
|
class Writer(models.Model):
|
||||||
name = models.CharField(maxlength=50)
|
name = models.CharField(maxlength=50, help_text='Use both first and last names.')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -38,13 +41,14 @@ class Article(models.Model):
|
|||||||
headline = models.CharField(maxlength=50)
|
headline = models.CharField(maxlength=50)
|
||||||
pub_date = models.DateField()
|
pub_date = models.DateField()
|
||||||
writer = models.ForeignKey(Writer)
|
writer = models.ForeignKey(Writer)
|
||||||
|
article = models.TextField()
|
||||||
categories = models.ManyToManyField(Category, blank=True)
|
categories = models.ManyToManyField(Category, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.headline
|
return self.headline
|
||||||
|
|
||||||
__test__ = {'API_TESTS': """
|
__test__ = {'API_TESTS': """
|
||||||
>>> from django.newforms import form_for_model, form_for_instance, BaseForm
|
>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
|
||||||
>>> import datetime
|
>>> import datetime
|
||||||
|
|
||||||
>>> Category.objects.all()
|
>>> Category.objects.all()
|
||||||
@ -67,35 +71,36 @@ __test__ = {'API_TESTS': """
|
|||||||
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
|
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
|
||||||
|
|
||||||
>>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
|
>>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
|
||||||
>>> f.errors
|
>>> f.is_valid()
|
||||||
{}
|
True
|
||||||
>>> f.clean_data
|
>>> f.clean_data
|
||||||
{'url': u'entertainment', 'name': u'Entertainment'}
|
{'url': u'entertainment', 'name': u'Entertainment'}
|
||||||
>>> obj = f.create()
|
>>> obj = f.save()
|
||||||
>>> obj
|
>>> obj
|
||||||
<Category: Entertainment>
|
<Category: Entertainment>
|
||||||
>>> Category.objects.all()
|
>>> Category.objects.all()
|
||||||
[<Category: Entertainment>]
|
[<Category: Entertainment>]
|
||||||
|
|
||||||
>>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
|
>>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
|
||||||
>>> f.errors
|
>>> f.is_valid()
|
||||||
{}
|
True
|
||||||
>>> f.clean_data
|
>>> f.clean_data
|
||||||
{'url': u'test', 'name': u"It's a test"}
|
{'url': u'test', 'name': u"It's a test"}
|
||||||
>>> obj = f.create()
|
>>> obj = f.save()
|
||||||
>>> obj
|
>>> obj
|
||||||
<Category: It's a test>
|
<Category: It's a test>
|
||||||
>>> Category.objects.all()
|
>>> Category.objects.all()
|
||||||
[<Category: Entertainment>, <Category: It's a test>]
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
|
||||||
If you call create() with save=False, then it will return an object that hasn't
|
If you call save() with commit=False, then it will return an object that
|
||||||
yet been saved. In this case, it's up to you to save it.
|
hasn't yet been saved to the database. In this case, it's up to you to call
|
||||||
|
save() on the resulting model instance.
|
||||||
>>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
|
>>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
|
||||||
>>> f.errors
|
>>> f.is_valid()
|
||||||
{}
|
True
|
||||||
>>> f.clean_data
|
>>> f.clean_data
|
||||||
{'url': u'third', 'name': u'Third test'}
|
{'url': u'third', 'name': u'Third test'}
|
||||||
>>> obj = f.create(save=False)
|
>>> obj = f.save(commit=False)
|
||||||
>>> obj
|
>>> obj
|
||||||
<Category: Third test>
|
<Category: Third test>
|
||||||
>>> Category.objects.all()
|
>>> Category.objects.all()
|
||||||
@ -104,17 +109,20 @@ yet been saved. In this case, it's up to you to save it.
|
|||||||
>>> Category.objects.all()
|
>>> Category.objects.all()
|
||||||
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
|
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
|
||||||
|
|
||||||
If you call create() with invalid data, you'll get a ValueError.
|
If you call save() with invalid data, you'll get a ValueError.
|
||||||
>>> f = CategoryForm({'name': '', 'url': 'foo'})
|
>>> f = CategoryForm({'name': '', 'url': 'foo'})
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{'name': [u'This field is required.']}
|
{'name': [u'This field is required.']}
|
||||||
>>> f.clean_data
|
>>> f.clean_data
|
||||||
>>> f.create()
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: 'CategoryForm' object has no attribute 'clean_data'
|
||||||
|
>>> f.save()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValueError: The Category could not be created because the data didn't validate.
|
ValueError: The Category could not be created because the data didn't validate.
|
||||||
>>> f = CategoryForm({'name': '', 'url': 'foo'})
|
>>> f = CategoryForm({'name': '', 'url': 'foo'})
|
||||||
>>> f.create()
|
>>> f.save()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValueError: The Category could not be created because the data didn't validate.
|
ValueError: The Category could not be created because the data didn't validate.
|
||||||
@ -137,11 +145,12 @@ represented by a ChoiceField.
|
|||||||
<option value="1">Mike Royko</option>
|
<option value="1">Mike Royko</option>
|
||||||
<option value="2">Bob Woodward</option>
|
<option value="2">Bob Woodward</option>
|
||||||
</select></td></tr>
|
</select></td></tr>
|
||||||
|
<tr><th>Article:</th><td><textarea name="article"></textarea></td></tr>
|
||||||
<tr><th>Categories:</th><td><select multiple="multiple" name="categories">
|
<tr><th>Categories:</th><td><select multiple="multiple" name="categories">
|
||||||
<option value="1">Entertainment</option>
|
<option value="1">Entertainment</option>
|
||||||
<option value="2">It's a test</option>
|
<option value="2">It's a test</option>
|
||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select></td></tr>
|
</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
|
||||||
|
|
||||||
You can pass a custom Form class to form_for_model. Make sure it's a
|
You can pass a custom Form class to form_for_model. Make sure it's a
|
||||||
subclass of BaseForm, not Form.
|
subclass of BaseForm, not Form.
|
||||||
@ -153,17 +162,16 @@ subclass of BaseForm, not Form.
|
|||||||
>>> f.say_hello()
|
>>> f.say_hello()
|
||||||
hello
|
hello
|
||||||
|
|
||||||
Use form_for_instance to create a Form from a model instance. There are two
|
Use form_for_instance to create a Form from a model instance. The difference
|
||||||
differences between this Form and one created via form_for_model. First, the
|
between this Form and one created via form_for_model is that the object's
|
||||||
object's current values are inserted as 'initial' data in each Field. Second,
|
current values are inserted as 'initial' data in each Field.
|
||||||
the Form gets an apply_changes() method instead of a create() method.
|
|
||||||
>>> w = Writer.objects.get(name='Mike Royko')
|
>>> w = Writer.objects.get(name='Mike Royko')
|
||||||
>>> RoykoForm = form_for_instance(w)
|
>>> RoykoForm = form_for_instance(w)
|
||||||
>>> f = RoykoForm(auto_id=False)
|
>>> f = RoykoForm(auto_id=False)
|
||||||
>>> print f
|
>>> print f
|
||||||
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /></td></tr>
|
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
|
||||||
|
|
||||||
>>> art = Article(headline='Test article', pub_date=datetime.date(1988, 1, 4), writer=w)
|
>>> art = Article(headline='Test article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.')
|
||||||
>>> art.save()
|
>>> art.save()
|
||||||
>>> art.id
|
>>> art.id
|
||||||
1
|
1
|
||||||
@ -177,15 +185,16 @@ the Form gets an apply_changes() method instead of a create() method.
|
|||||||
<option value="1" selected="selected">Mike Royko</option>
|
<option value="1" selected="selected">Mike Royko</option>
|
||||||
<option value="2">Bob Woodward</option>
|
<option value="2">Bob Woodward</option>
|
||||||
</select></li>
|
</select></li>
|
||||||
|
<li>Article: <textarea name="article">Hello.</textarea></li>
|
||||||
<li>Categories: <select multiple="multiple" name="categories">
|
<li>Categories: <select multiple="multiple" name="categories">
|
||||||
<option value="1">Entertainment</option>
|
<option value="1">Entertainment</option>
|
||||||
<option value="2">It's a test</option>
|
<option value="2">It's a test</option>
|
||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select></li>
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1'})
|
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1', 'article': 'Hello.'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> new_art = f.apply_changes()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
1
|
1
|
||||||
>>> new_art = Article.objects.get(id=1)
|
>>> new_art = Article.objects.get(id=1)
|
||||||
@ -208,10 +217,68 @@ Add some categories and test the many-to-many form output.
|
|||||||
<option value="1" selected="selected">Mike Royko</option>
|
<option value="1" selected="selected">Mike Royko</option>
|
||||||
<option value="2">Bob Woodward</option>
|
<option value="2">Bob Woodward</option>
|
||||||
</select></li>
|
</select></li>
|
||||||
|
<li>Article: <textarea name="article">Hello.</textarea></li>
|
||||||
<li>Categories: <select multiple="multiple" name="categories">
|
<li>Categories: <select multiple="multiple" name="categories">
|
||||||
<option value="1" selected="selected">Entertainment</option>
|
<option value="1" selected="selected">Entertainment</option>
|
||||||
<option value="2">It's a test</option>
|
<option value="2">It's a test</option>
|
||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select></li>
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
|
|
||||||
|
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04',
|
||||||
|
... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']})
|
||||||
|
>>> new_art = f.save()
|
||||||
|
>>> new_art.id
|
||||||
|
1
|
||||||
|
>>> new_art = Article.objects.get(id=1)
|
||||||
|
>>> new_art.categories.all()
|
||||||
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
|
||||||
|
Now, submit form data with no categories. This deletes the existing categories.
|
||||||
|
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04',
|
||||||
|
... 'writer': u'1', 'article': u'Hello.'})
|
||||||
|
>>> new_art = f.save()
|
||||||
|
>>> new_art.id
|
||||||
|
1
|
||||||
|
>>> new_art = Article.objects.get(id=1)
|
||||||
|
>>> new_art.categories.all()
|
||||||
|
[]
|
||||||
|
|
||||||
|
Create a new article, with categories, via the form.
|
||||||
|
>>> ArticleForm = form_for_model(Article)
|
||||||
|
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'pub_date': u'1967-11-01',
|
||||||
|
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
||||||
|
>>> new_art = f.save()
|
||||||
|
>>> new_art.id
|
||||||
|
2
|
||||||
|
>>> new_art = Article.objects.get(id=2)
|
||||||
|
>>> new_art.categories.all()
|
||||||
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
|
||||||
|
Create a new article, with no categories, via the form.
|
||||||
|
>>> ArticleForm = form_for_model(Article)
|
||||||
|
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'pub_date': u'1967-11-01',
|
||||||
|
... 'writer': u'1', 'article': u'Test.'})
|
||||||
|
>>> new_art = f.save()
|
||||||
|
>>> new_art.id
|
||||||
|
3
|
||||||
|
>>> new_art = Article.objects.get(id=3)
|
||||||
|
>>> new_art.categories.all()
|
||||||
|
[]
|
||||||
|
|
||||||
|
Here, we define a custom Form. Because it happens to have the same fields as
|
||||||
|
the Category model, we can use save_instance() to apply its changes to an
|
||||||
|
existing Category instance.
|
||||||
|
>>> class ShortCategory(Form):
|
||||||
|
... name = CharField(max_length=5)
|
||||||
|
... url = CharField(max_length=3)
|
||||||
|
>>> cat = Category.objects.get(name='Third test')
|
||||||
|
>>> cat
|
||||||
|
<Category: Third test>
|
||||||
|
>>> cat.id
|
||||||
|
3
|
||||||
|
>>> sc = ShortCategory({'name': 'Third', 'url': '3rd'})
|
||||||
|
>>> save_instance(sc, cat)
|
||||||
|
<Category: Third>
|
||||||
|
>>> Category.objects.get(id=3)
|
||||||
|
<Category: Third>
|
||||||
"""}
|
"""}
|
||||||
|
@ -69,6 +69,21 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))
|
>>> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))
|
||||||
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
|
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
|
||||||
|
|
||||||
|
# You could also use "in" to accomplish the same as above.
|
||||||
|
>>> Article.objects.filter(pk__in=[1,2,3])
|
||||||
|
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
|
||||||
|
|
||||||
|
>>> Article.objects.filter(pk__in=[1,2,3,4])
|
||||||
|
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
|
||||||
|
|
||||||
|
# Passing "in" an empty list returns no results ...
|
||||||
|
>>> Article.objects.filter(pk__in=[])
|
||||||
|
[]
|
||||||
|
|
||||||
|
# ... but can return results if we OR it with another query.
|
||||||
|
>>> Article.objects.filter(Q(pk__in=[]) | Q(headline__icontains='goodbye'))
|
||||||
|
[<Article: Goodbye>, <Article: Hello and goodbye>]
|
||||||
|
|
||||||
# Q arg objects are ANDed
|
# Q arg objects are ANDed
|
||||||
>>> Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye'))
|
>>> Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye'))
|
||||||
[<Article: Hello and goodbye>]
|
[<Article: Hello and goodbye>]
|
||||||
|
@ -37,6 +37,13 @@ class Article(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.headline
|
return self.headline
|
||||||
|
|
||||||
|
class AuthorProfile(models.Model):
|
||||||
|
author = models.OneToOneField(Author)
|
||||||
|
date_of_birth = models.DateField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Profile of %s" % self.author
|
||||||
|
|
||||||
__test__ = {'API_TESTS':"""
|
__test__ = {'API_TESTS':"""
|
||||||
# Create some data:
|
# Create some data:
|
||||||
>>> from datetime import datetime
|
>>> from datetime import datetime
|
||||||
@ -118,4 +125,18 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> Article.objects.all()
|
>>> Article.objects.all()
|
||||||
[<Article: Just kidding; I love TV poker>, <Article: Time to reform copyright>]
|
[<Article: Just kidding; I love TV poker>, <Article: Time to reform copyright>]
|
||||||
|
|
||||||
|
# If you use your own primary key field (such as a OneToOneField),
|
||||||
|
# it doesn't appear in the serialized field list - it replaces the
|
||||||
|
# pk identifier.
|
||||||
|
>>> profile = AuthorProfile(author=joe, date_of_birth=datetime(1970,1,1))
|
||||||
|
>>> profile.save()
|
||||||
|
|
||||||
|
>>> json = serializers.serialize("json", AuthorProfile.objects.all())
|
||||||
|
>>> json
|
||||||
|
'[{"pk": "1", "model": "serializers.authorprofile", "fields": {"date_of_birth": "1970-01-01"}}]'
|
||||||
|
|
||||||
|
>>> for obj in serializers.deserialize("json", json):
|
||||||
|
... print obj
|
||||||
|
<DeserializedObject: Profile of Joe>
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -26,10 +26,10 @@ def redirect_view(request):
|
|||||||
"A view that redirects all requests to the GET view"
|
"A view that redirects all requests to the GET view"
|
||||||
return HttpResponseRedirect('/test_client/get_view/')
|
return HttpResponseRedirect('/test_client/get_view/')
|
||||||
|
|
||||||
@login_required
|
|
||||||
def login_protected_view(request):
|
def login_protected_view(request):
|
||||||
"A simple view that is login protected."
|
"A simple view that is login protected."
|
||||||
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
|
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
|
||||||
c = Context({'user': request.user})
|
c = Context({'user': request.user})
|
||||||
|
|
||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
|
login_protected_view = login_required(login_protected_view)
|
@ -106,6 +106,46 @@ u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u
|
|||||||
>>> w.render('email', '', attrs={'class': 'special'})
|
>>> w.render('email', '', attrs={'class': 'special'})
|
||||||
u'<input type="hidden" class="special" name="email" />'
|
u'<input type="hidden" class="special" name="email" />'
|
||||||
|
|
||||||
|
# MultipleHiddenInput Widget ##################################################
|
||||||
|
|
||||||
|
>>> w = MultipleHiddenInput()
|
||||||
|
>>> w.render('email', [])
|
||||||
|
u''
|
||||||
|
>>> w.render('email', None)
|
||||||
|
u''
|
||||||
|
>>> w.render('email', ['test@example.com'])
|
||||||
|
u'<input type="hidden" name="email" value="test@example.com" />'
|
||||||
|
>>> w.render('email', ['some "quoted" & ampersanded value'])
|
||||||
|
u'<input type="hidden" name="email" value="some "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 ############################################################
|
# FileInput Widget ############################################################
|
||||||
|
|
||||||
>>> w = FileInput()
|
>>> w = FileInput()
|
||||||
@ -296,6 +336,60 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
|
|||||||
>>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
|
>>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
|
||||||
u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
|
u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
|
||||||
|
|
||||||
|
If choices is passed to the constructor and is a generator, it can be iterated
|
||||||
|
over multiple times without getting consumed:
|
||||||
|
>>> w = Select(choices=get_choices())
|
||||||
|
>>> print w.render('num', 2)
|
||||||
|
<select name="num">
|
||||||
|
<option value="0">0</option>
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2" selected="selected">2</option>
|
||||||
|
<option value="3">3</option>
|
||||||
|
<option value="4">4</option>
|
||||||
|
</select>
|
||||||
|
>>> print w.render('num', 3)
|
||||||
|
<select name="num">
|
||||||
|
<option value="0">0</option>
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
<option value="3" selected="selected">3</option>
|
||||||
|
<option value="4">4</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
# NullBooleanSelect Widget ####################################################
|
||||||
|
|
||||||
|
>>> w = NullBooleanSelect()
|
||||||
|
>>> print w.render('is_cool', True)
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1">Unknown</option>
|
||||||
|
<option value="2" selected="selected">Yes</option>
|
||||||
|
<option value="3">No</option>
|
||||||
|
</select>
|
||||||
|
>>> print w.render('is_cool', False)
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1">Unknown</option>
|
||||||
|
<option value="2">Yes</option>
|
||||||
|
<option value="3" selected="selected">No</option>
|
||||||
|
</select>
|
||||||
|
>>> print w.render('is_cool', None)
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1" selected="selected">Unknown</option>
|
||||||
|
<option value="2">Yes</option>
|
||||||
|
<option value="3">No</option>
|
||||||
|
</select>
|
||||||
|
>>> print w.render('is_cool', '2')
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1">Unknown</option>
|
||||||
|
<option value="2" selected="selected">Yes</option>
|
||||||
|
<option value="3">No</option>
|
||||||
|
</select>
|
||||||
|
>>> print w.render('is_cool', '3')
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1">Unknown</option>
|
||||||
|
<option value="2">Yes</option>
|
||||||
|
<option value="3" selected="selected">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
# SelectMultiple Widget #######################################################
|
# SelectMultiple Widget #######################################################
|
||||||
|
|
||||||
>>> w = SelectMultiple()
|
>>> w = SelectMultiple()
|
||||||
@ -527,12 +621,16 @@ True
|
|||||||
>>> r[1].is_checked()
|
>>> r[1].is_checked()
|
||||||
False
|
False
|
||||||
>>> r[1].name, r[1].value, r[1].choice_value, r[1].choice_label
|
>>> r[1].name, r[1].value, r[1].choice_value, r[1].choice_label
|
||||||
('beatle', u'J', 'P', 'Paul')
|
('beatle', u'J', u'P', u'Paul')
|
||||||
>>> r[10]
|
>>> r[10]
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
IndexError: list index out of range
|
IndexError: list index out of range
|
||||||
|
|
||||||
|
>>> w = RadioSelect()
|
||||||
|
>>> unicode(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]))
|
||||||
|
u'<ul>\n<li><label><input checked="checked" type="radio" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="radio" name="email" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>'
|
||||||
|
|
||||||
# CheckboxSelectMultiple Widget ###############################################
|
# CheckboxSelectMultiple Widget ###############################################
|
||||||
|
|
||||||
>>> w = CheckboxSelectMultiple()
|
>>> w = CheckboxSelectMultiple()
|
||||||
@ -640,6 +738,39 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
|
|||||||
>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
|
>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
|
||||||
u'<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>\n<li><label><input type="checkbox" name="nums" value="2" /> 2</label></li>\n<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>\n<li><label><input checked="checked" type="checkbox" name="nums" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="checkbox" name="nums" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>'
|
u'<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>\n<li><label><input type="checkbox" name="nums" value="2" /> 2</label></li>\n<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>\n<li><label><input checked="checked" type="checkbox" name="nums" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="checkbox" name="nums" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>'
|
||||||
|
|
||||||
|
# MultiWidget #################################################################
|
||||||
|
|
||||||
|
>>> class MyMultiWidget(MultiWidget):
|
||||||
|
... def decompress(self, value):
|
||||||
|
... if value:
|
||||||
|
... return value.split('__')
|
||||||
|
... return ['', '']
|
||||||
|
... def format_output(self, rendered_widgets):
|
||||||
|
... return u'<br />'.join(rendered_widgets)
|
||||||
|
>>> w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})))
|
||||||
|
>>> w.render('name', ['john', 'lennon'])
|
||||||
|
u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />'
|
||||||
|
>>> w.render('name', 'john__lennon')
|
||||||
|
u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />'
|
||||||
|
|
||||||
|
# SplitDateTimeWidget #########################################################
|
||||||
|
|
||||||
|
>>> w = SplitDateTimeWidget()
|
||||||
|
>>> w.render('date', '')
|
||||||
|
u'<input type="text" name="date_0" /><input type="text" name="date_1" />'
|
||||||
|
>>> w.render('date', None)
|
||||||
|
u'<input type="text" name="date_0" /><input type="text" name="date_1" />'
|
||||||
|
>>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30))
|
||||||
|
u'<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />'
|
||||||
|
>>> w.render('date', [datetime.date(2006, 1, 10), datetime.time(7, 30)])
|
||||||
|
u'<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />'
|
||||||
|
|
||||||
|
You can also pass 'attrs' to the constructor. In this case, the attrs will be
|
||||||
|
included on both widgets.
|
||||||
|
>>> w = SplitDateTimeWidget(attrs={'class': 'pretty'})
|
||||||
|
>>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30))
|
||||||
|
u'<input type="text" class="pretty" value="2006-01-10" name="date_0" /><input type="text" class="pretty" value="07:30:00" name="date_1" />'
|
||||||
|
|
||||||
##########
|
##########
|
||||||
# Fields #
|
# Fields #
|
||||||
##########
|
##########
|
||||||
@ -766,9 +897,11 @@ ValidationError: [u'Enter a whole number.']
|
|||||||
|
|
||||||
>>> f = IntegerField(required=False)
|
>>> f = IntegerField(required=False)
|
||||||
>>> f.clean('')
|
>>> f.clean('')
|
||||||
u''
|
>>> repr(f.clean(''))
|
||||||
|
'None'
|
||||||
>>> f.clean(None)
|
>>> f.clean(None)
|
||||||
u''
|
>>> repr(f.clean(None))
|
||||||
|
'None'
|
||||||
>>> f.clean('1')
|
>>> f.clean('1')
|
||||||
1
|
1
|
||||||
>>> isinstance(f.clean('1'), int)
|
>>> isinstance(f.clean('1'), int)
|
||||||
@ -1285,6 +1418,11 @@ ValidationError: [u'This URL appears to be a broken link.']
|
|||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'This URL appears to be a broken link.']
|
ValidationError: [u'This URL appears to be a broken link.']
|
||||||
|
>>> f = URLField(verify_exists=True, required=False)
|
||||||
|
>>> f.clean('')
|
||||||
|
u''
|
||||||
|
>>> f.clean('http://www.google.com') # This will fail if there's no Internet connection
|
||||||
|
u'http://www.google.com'
|
||||||
|
|
||||||
EmailField also access min_length and max_length parameters, for convenience.
|
EmailField also access min_length and max_length parameters, for convenience.
|
||||||
>>> f = URLField(min_length=15, max_length=20)
|
>>> f = URLField(min_length=15, max_length=20)
|
||||||
@ -1379,6 +1517,20 @@ Traceback (most recent call last):
|
|||||||
...
|
...
|
||||||
ValidationError: [u'Select a valid choice. John is not one of the available choices.']
|
ValidationError: [u'Select a valid choice. John is not one of the available choices.']
|
||||||
|
|
||||||
|
# NullBooleanField ############################################################
|
||||||
|
|
||||||
|
>>> f = NullBooleanField()
|
||||||
|
>>> f.clean('')
|
||||||
|
>>> f.clean(True)
|
||||||
|
True
|
||||||
|
>>> f.clean(False)
|
||||||
|
False
|
||||||
|
>>> f.clean(None)
|
||||||
|
>>> f.clean('1')
|
||||||
|
>>> f.clean('2')
|
||||||
|
>>> f.clean('3')
|
||||||
|
>>> f.clean('hello')
|
||||||
|
|
||||||
# MultipleChoiceField #########################################################
|
# MultipleChoiceField #########################################################
|
||||||
|
|
||||||
>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')])
|
>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')])
|
||||||
@ -1485,6 +1637,58 @@ u''
|
|||||||
>>> f.clean(None)
|
>>> f.clean(None)
|
||||||
u''
|
u''
|
||||||
|
|
||||||
|
# SplitDateTimeField ##########################################################
|
||||||
|
|
||||||
|
>>> f = SplitDateTimeField()
|
||||||
|
>>> f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)])
|
||||||
|
datetime.datetime(2006, 1, 10, 7, 30)
|
||||||
|
>>> f.clean(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean('')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean('hello')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a list of values.']
|
||||||
|
>>> f.clean(['hello', 'there'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid date.', u'Enter a valid time.']
|
||||||
|
>>> f.clean(['2006-01-10', 'there'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid time.']
|
||||||
|
>>> f.clean(['hello', '07:30'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid date.']
|
||||||
|
|
||||||
|
>>> f = SplitDateTimeField(required=False)
|
||||||
|
>>> f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)])
|
||||||
|
datetime.datetime(2006, 1, 10, 7, 30)
|
||||||
|
>>> f.clean(None)
|
||||||
|
>>> f.clean('')
|
||||||
|
>>> f.clean('hello')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a list of values.']
|
||||||
|
>>> f.clean(['hello', 'there'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid date.', u'Enter a valid time.']
|
||||||
|
>>> f.clean(['2006-01-10', 'there'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid time.']
|
||||||
|
>>> f.clean(['hello', '07:30'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid date.']
|
||||||
|
|
||||||
#########
|
#########
|
||||||
# Forms #
|
# Forms #
|
||||||
#########
|
#########
|
||||||
@ -1502,6 +1706,8 @@ You can pass it data in __init__(), as a dictionary.
|
|||||||
|
|
||||||
Pass a dictionary to a Form's __init__().
|
Pass a dictionary to a Form's __init__().
|
||||||
>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
|
>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
|
||||||
|
>>> p.is_bound
|
||||||
|
True
|
||||||
>>> p.errors
|
>>> p.errors
|
||||||
{}
|
{}
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
@ -1540,10 +1746,16 @@ Birthday 1940-10-9
|
|||||||
|
|
||||||
Empty dictionaries are valid, too.
|
Empty dictionaries are valid, too.
|
||||||
>>> p = Person({})
|
>>> p = Person({})
|
||||||
|
>>> p.is_bound
|
||||||
|
True
|
||||||
>>> p.errors
|
>>> p.errors
|
||||||
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
|
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
False
|
False
|
||||||
|
>>> p.clean_data
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: 'Person' object has no attribute 'clean_data'
|
||||||
>>> print p
|
>>> print p
|
||||||
<tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr>
|
<tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr>
|
||||||
<tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr>
|
<tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr>
|
||||||
@ -1565,13 +1777,19 @@ False
|
|||||||
<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p>
|
<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p>
|
||||||
|
|
||||||
If you don't pass any values to the Form's __init__(), or if you pass None,
|
If you don't pass any values to the Form's __init__(), or if you pass None,
|
||||||
the Form won't do any validation. Form.errors will be an empty dictionary *but*
|
the Form will be considered unbound and won't do any validation. Form.errors
|
||||||
Form.is_valid() will return False.
|
will be an empty dictionary *but* Form.is_valid() will return False.
|
||||||
>>> p = Person()
|
>>> p = Person()
|
||||||
|
>>> p.is_bound
|
||||||
|
False
|
||||||
>>> p.errors
|
>>> p.errors
|
||||||
{}
|
{}
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
False
|
False
|
||||||
|
>>> p.clean_data
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: 'Person' object has no attribute 'clean_data'
|
||||||
>>> print p
|
>>> print p
|
||||||
<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
|
<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
|
||||||
<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
|
<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
|
||||||
@ -1611,8 +1829,9 @@ u'<ul class="errorlist"><li>first_name<ul class="errorlist"><li>This field is re
|
|||||||
* birthday
|
* birthday
|
||||||
* This field is required.
|
* This field is required.
|
||||||
>>> p.clean_data
|
>>> p.clean_data
|
||||||
>>> repr(p.clean_data)
|
Traceback (most recent call last):
|
||||||
'None'
|
...
|
||||||
|
AttributeError: 'Person' object has no attribute 'clean_data'
|
||||||
>>> p['first_name'].errors
|
>>> p['first_name'].errors
|
||||||
[u'This field is required.']
|
[u'This field is required.']
|
||||||
>>> p['first_name'].errors.as_ul()
|
>>> p['first_name'].errors.as_ul()
|
||||||
@ -1628,6 +1847,17 @@ u'* This field is required.'
|
|||||||
>>> print p['birthday']
|
>>> print p['birthday']
|
||||||
<input type="text" name="birthday" id="id_birthday" />
|
<input type="text" name="birthday" id="id_birthday" />
|
||||||
|
|
||||||
|
clean_data will always *only* contain a key for fields defined in the
|
||||||
|
Form, even if you pass extra data when you define the Form. In this
|
||||||
|
example, we pass a bunch of extra fields to the form constructor,
|
||||||
|
but clean_data contains only the form's fields.
|
||||||
|
>>> data = {'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9', 'extra1': 'hello', 'extra2': 'hello'}
|
||||||
|
>>> p = Person(data)
|
||||||
|
>>> p.is_valid()
|
||||||
|
True
|
||||||
|
>>> p.clean_data
|
||||||
|
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
||||||
|
|
||||||
"auto_id" tells the Form to add an "id" attribute to each form element.
|
"auto_id" tells the Form to add an "id" attribute to each form element.
|
||||||
If it's a string that contains '%s', Django will use that as a format string
|
If it's a string that contains '%s', Django will use that as a format string
|
||||||
into which the field's name will be inserted. It will also put a <label> around
|
into which the field's name will be inserted. It will also put a <label> around
|
||||||
@ -1753,6 +1983,57 @@ For a form with a <select>, use ChoiceField:
|
|||||||
<option value="J">Java</option>
|
<option value="J">Java</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
You can specify widget attributes in the Widget constructor.
|
||||||
|
>>> class FrameworkForm(Form):
|
||||||
|
... name = CharField()
|
||||||
|
... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=Select(attrs={'class': 'foo'}))
|
||||||
|
>>> f = FrameworkForm(auto_id=False)
|
||||||
|
>>> print f['language']
|
||||||
|
<select class="foo" name="language">
|
||||||
|
<option value="P">Python</option>
|
||||||
|
<option value="J">Java</option>
|
||||||
|
</select>
|
||||||
|
>>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False)
|
||||||
|
>>> print f['language']
|
||||||
|
<select class="foo" name="language">
|
||||||
|
<option value="P" selected="selected">Python</option>
|
||||||
|
<option value="J">Java</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
When passing a custom widget instance to ChoiceField, note that setting
|
||||||
|
'choices' on the widget is meaningless. The widget will use the choices
|
||||||
|
defined on the Field, not the ones defined on the Widget.
|
||||||
|
>>> class FrameworkForm(Form):
|
||||||
|
... name = CharField()
|
||||||
|
... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=Select(choices=[('R', 'Ruby'), ('P', 'Perl')], attrs={'class': 'foo'}))
|
||||||
|
>>> f = FrameworkForm(auto_id=False)
|
||||||
|
>>> print f['language']
|
||||||
|
<select class="foo" name="language">
|
||||||
|
<option value="P">Python</option>
|
||||||
|
<option value="J">Java</option>
|
||||||
|
</select>
|
||||||
|
>>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False)
|
||||||
|
>>> print f['language']
|
||||||
|
<select class="foo" name="language">
|
||||||
|
<option value="P" selected="selected">Python</option>
|
||||||
|
<option value="J">Java</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
You can set a ChoiceField's choices after the fact.
|
||||||
|
>>> class FrameworkForm(Form):
|
||||||
|
... name = CharField()
|
||||||
|
... language = ChoiceField()
|
||||||
|
>>> f = FrameworkForm(auto_id=False)
|
||||||
|
>>> print f['language']
|
||||||
|
<select name="language">
|
||||||
|
</select>
|
||||||
|
>>> f.fields['language'].choices = [('P', 'Python'), ('J', 'Java')]
|
||||||
|
>>> print f['language']
|
||||||
|
<select name="language">
|
||||||
|
<option value="P">Python</option>
|
||||||
|
<option value="J">Java</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
Add widget=RadioSelect to use that widget with a ChoiceField.
|
Add widget=RadioSelect to use that widget with a ChoiceField.
|
||||||
>>> class FrameworkForm(Form):
|
>>> class FrameworkForm(Form):
|
||||||
... name = CharField()
|
... name = CharField()
|
||||||
@ -1834,6 +2115,17 @@ MultipleChoiceField is a special case, as its data is required to be a list:
|
|||||||
<option value="P" selected="selected">Paul McCartney</option>
|
<option value="P" selected="selected">Paul McCartney</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
MultipleChoiceField rendered as_hidden() is a special case. Because it can
|
||||||
|
have multiple values, its as_hidden() renders multiple <input type="hidden">
|
||||||
|
tags.
|
||||||
|
>>> f = SongForm({'name': 'Yesterday', 'composers': ['P']}, auto_id=False)
|
||||||
|
>>> print f['composers'].as_hidden()
|
||||||
|
<input type="hidden" name="composers" value="P" />
|
||||||
|
>>> f = SongForm({'name': 'From Me To You', 'composers': ['P', 'J']}, auto_id=False)
|
||||||
|
>>> print f['composers'].as_hidden()
|
||||||
|
<input type="hidden" name="composers" value="P" />
|
||||||
|
<input type="hidden" name="composers" value="J" />
|
||||||
|
|
||||||
MultipleChoiceField can also be used with the CheckboxSelectMultiple widget.
|
MultipleChoiceField can also be used with the CheckboxSelectMultiple widget.
|
||||||
>>> class SongForm(Form):
|
>>> class SongForm(Form):
|
||||||
... name = CharField()
|
... name = CharField()
|
||||||
@ -1857,6 +2149,16 @@ MultipleChoiceField can also be used with the CheckboxSelectMultiple widget.
|
|||||||
<li><label><input checked="checked" type="checkbox" name="composers" value="P" /> Paul McCartney</label></li>
|
<li><label><input checked="checked" type="checkbox" name="composers" value="P" /> Paul McCartney</label></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
Regarding auto_id, CheckboxSelectMultiple is a special case. Each checkbox
|
||||||
|
gets a distinct ID, formed by appending an underscore plus the checkbox's
|
||||||
|
zero-based index.
|
||||||
|
>>> f = SongForm(auto_id='%s_id')
|
||||||
|
>>> print f['composers']
|
||||||
|
<ul>
|
||||||
|
<li><label><input type="checkbox" name="composers" value="J" id="composers_id_0" /> John Lennon</label></li>
|
||||||
|
<li><label><input type="checkbox" name="composers" value="P" id="composers_id_1" /> Paul McCartney</label></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
Data for a MultipleChoiceField should be a list. QueryDict and MultiValueDict
|
Data for a MultipleChoiceField should be a list. QueryDict and MultiValueDict
|
||||||
conveniently work with this.
|
conveniently work with this.
|
||||||
>>> data = {'name': 'Yesterday', 'composers': ['J', 'P']}
|
>>> data = {'name': 'Yesterday', 'composers': ['J', 'P']}
|
||||||
@ -1869,11 +2171,20 @@ conveniently work with this.
|
|||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
>>> from django.utils.datastructures import MultiValueDict
|
>>> from django.utils.datastructures import MultiValueDict
|
||||||
>>> data = MultiValueDict(dict(name='Yesterday', composers=['J', 'P']))
|
>>> data = MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P']))
|
||||||
>>> f = SongForm(data)
|
>>> f = SongForm(data)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
The MultipleHiddenInput widget renders multiple values as hidden fields.
|
||||||
|
>>> class SongFormHidden(Form):
|
||||||
|
... name = CharField()
|
||||||
|
... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=MultipleHiddenInput)
|
||||||
|
>>> f = SongFormHidden(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])), auto_id=False)
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li>Name: <input type="text" name="name" value="Yesterday" /><input type="hidden" name="composers" value="J" />
|
||||||
|
<input type="hidden" name="composers" value="P" /></li>
|
||||||
|
|
||||||
When using CheckboxSelectMultiple, the framework expects a list of input and
|
When using CheckboxSelectMultiple, the framework expects a list of input and
|
||||||
returns a list of input.
|
returns a list of input.
|
||||||
>>> f = SongForm({'name': 'Yesterday'}, auto_id=False)
|
>>> f = SongForm({'name': 'Yesterday'}, auto_id=False)
|
||||||
@ -1890,6 +2201,8 @@ returns a list of input.
|
|||||||
>>> f.clean_data
|
>>> f.clean_data
|
||||||
{'composers': [u'J', u'P'], 'name': u'Yesterday'}
|
{'composers': [u'J', u'P'], 'name': u'Yesterday'}
|
||||||
|
|
||||||
|
# Validating multiple fields in relation to another ###########################
|
||||||
|
|
||||||
There are a couple of ways to do multiple-field validation. If you want the
|
There are a couple of ways to do multiple-field validation. If you want the
|
||||||
validation message to be associated with a particular field, implement the
|
validation message to be associated with a particular field, implement the
|
||||||
clean_XXX() method on the Form, where XXX is the field name. As in
|
clean_XXX() method on the Form, where XXX is the field name. As in
|
||||||
@ -1964,6 +2277,8 @@ Form.clean() is required to return a dictionary of all clean data.
|
|||||||
>>> f.clean_data
|
>>> f.clean_data
|
||||||
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
||||||
|
|
||||||
|
# Dynamic construction ########################################################
|
||||||
|
|
||||||
It's possible to construct a Form dynamically by adding to the self.fields
|
It's possible to construct a Form dynamically by adding to the self.fields
|
||||||
dictionary in __init__(). Don't forget to call Form.__init__() within the
|
dictionary in __init__(). Don't forget to call Form.__init__() within the
|
||||||
subclass' __init__().
|
subclass' __init__().
|
||||||
@ -1979,6 +2294,46 @@ subclass' __init__().
|
|||||||
<tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr>
|
<tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr>
|
||||||
<tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr>
|
<tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr>
|
||||||
|
|
||||||
|
Instances of a dynamic Form do not persist fields from one Form instance to
|
||||||
|
the next.
|
||||||
|
>>> class MyForm(Form):
|
||||||
|
... def __init__(self, data=None, auto_id=False, field_list=[]):
|
||||||
|
... Form.__init__(self, data, auto_id)
|
||||||
|
... for field in field_list:
|
||||||
|
... self.fields[field[0]] = field[1]
|
||||||
|
>>> field_list = [('field1', CharField()), ('field2', CharField())]
|
||||||
|
>>> my_form = MyForm(field_list=field_list)
|
||||||
|
>>> print my_form
|
||||||
|
<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr>
|
||||||
|
<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr>
|
||||||
|
>>> field_list = [('field3', CharField()), ('field4', CharField())]
|
||||||
|
>>> my_form = MyForm(field_list=field_list)
|
||||||
|
>>> print my_form
|
||||||
|
<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr>
|
||||||
|
<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr>
|
||||||
|
|
||||||
|
>>> class MyForm(Form):
|
||||||
|
... default_field_1 = CharField()
|
||||||
|
... default_field_2 = CharField()
|
||||||
|
... def __init__(self, data=None, auto_id=False, field_list=[]):
|
||||||
|
... Form.__init__(self, data, auto_id)
|
||||||
|
... for field in field_list:
|
||||||
|
... self.fields[field[0]] = field[1]
|
||||||
|
>>> field_list = [('field1', CharField()), ('field2', CharField())]
|
||||||
|
>>> my_form = MyForm(field_list=field_list)
|
||||||
|
>>> print my_form
|
||||||
|
<tr><th>Default field 1:</th><td><input type="text" name="default_field_1" /></td></tr>
|
||||||
|
<tr><th>Default field 2:</th><td><input type="text" name="default_field_2" /></td></tr>
|
||||||
|
<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr>
|
||||||
|
<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr>
|
||||||
|
>>> field_list = [('field3', CharField()), ('field4', CharField())]
|
||||||
|
>>> my_form = MyForm(field_list=field_list)
|
||||||
|
>>> print my_form
|
||||||
|
<tr><th>Default field 1:</th><td><input type="text" name="default_field_1" /></td></tr>
|
||||||
|
<tr><th>Default field 2:</th><td><input type="text" name="default_field_2" /></td></tr>
|
||||||
|
<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr>
|
||||||
|
<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr>
|
||||||
|
|
||||||
HiddenInput widgets are displayed differently in the as_table(), as_ul()
|
HiddenInput widgets are displayed differently in the as_table(), as_ul()
|
||||||
and as_p() output of a Form -- their verbose names are not displayed, and a
|
and as_p() output of a Form -- their verbose names are not displayed, and a
|
||||||
separate row is not displayed. They're displayed in the last row of the
|
separate row is not displayed. They're displayed in the last row of the
|
||||||
@ -2200,6 +2555,96 @@ validation error rather than using the initial value for 'username'.
|
|||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
False
|
False
|
||||||
|
|
||||||
|
# Dynamic initial data ########################################################
|
||||||
|
|
||||||
|
The previous technique dealt with "hard-coded" initial data, but it's also
|
||||||
|
possible to specify initial data after you've already created the Form class
|
||||||
|
(i.e., at runtime). Use the 'initial' parameter to the Form constructor. This
|
||||||
|
should be a dictionary containing initial values for one or more fields in the
|
||||||
|
form, keyed by field name.
|
||||||
|
|
||||||
|
>>> class UserRegistration(Form):
|
||||||
|
... username = CharField(max_length=10)
|
||||||
|
... password = CharField(widget=PasswordInput)
|
||||||
|
|
||||||
|
Here, we're not submitting any data, so the initial value will be displayed.
|
||||||
|
>>> p = UserRegistration(initial={'username': 'django'}, auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li>
|
||||||
|
<li>Password: <input type="password" name="password" /></li>
|
||||||
|
>>> p = UserRegistration(initial={'username': 'stephane'}, auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li>
|
||||||
|
<li>Password: <input type="password" name="password" /></li>
|
||||||
|
|
||||||
|
The 'initial' parameter is meaningless if you pass data.
|
||||||
|
>>> p = UserRegistration({}, initial={'username': 'django'}, auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li>
|
||||||
|
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
|
||||||
|
>>> p = UserRegistration({'username': u''}, initial={'username': 'django'}, auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li>
|
||||||
|
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
|
||||||
|
>>> p = UserRegistration({'username': u'foo'}, initial={'username': 'django'}, auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li>
|
||||||
|
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
|
||||||
|
|
||||||
|
A dynamic 'initial' value is *not* used as a fallback if data is not provided.
|
||||||
|
In this example, we don't provide a value for 'username', and the form raises a
|
||||||
|
validation error rather than using the initial value for 'username'.
|
||||||
|
>>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'})
|
||||||
|
>>> p.errors
|
||||||
|
{'username': [u'This field is required.']}
|
||||||
|
>>> p.is_valid()
|
||||||
|
False
|
||||||
|
|
||||||
|
If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(),
|
||||||
|
then the latter will get precedence.
|
||||||
|
>>> class UserRegistration(Form):
|
||||||
|
... username = CharField(max_length=10, initial='django')
|
||||||
|
... password = CharField(widget=PasswordInput)
|
||||||
|
>>> p = UserRegistration(initial={'username': 'babik'}, auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li>
|
||||||
|
<li>Password: <input type="password" name="password" /></li>
|
||||||
|
|
||||||
|
# Help text ###################################################################
|
||||||
|
|
||||||
|
You can specify descriptive text for a field by using the 'help_text' argument
|
||||||
|
to a Field class. This help text is displayed when a Form is rendered.
|
||||||
|
>>> class UserRegistration(Form):
|
||||||
|
... username = CharField(max_length=10, help_text='e.g., user@example.com')
|
||||||
|
... password = CharField(widget=PasswordInput, help_text='Choose wisely.')
|
||||||
|
>>> p = UserRegistration(auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li>
|
||||||
|
<li>Password: <input type="password" name="password" /> Choose wisely.</li>
|
||||||
|
>>> print p.as_p()
|
||||||
|
<p>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</p>
|
||||||
|
<p>Password: <input type="password" name="password" /> Choose wisely.</p>
|
||||||
|
>>> print p.as_table()
|
||||||
|
<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /><br />e.g., user@example.com</td></tr>
|
||||||
|
<tr><th>Password:</th><td><input type="password" name="password" /><br />Choose wisely.</td></tr>
|
||||||
|
|
||||||
|
The help text is displayed whether or not data is provided for the form.
|
||||||
|
>>> p = UserRegistration({'username': u'foo'}, auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li>Username: <input type="text" name="username" value="foo" maxlength="10" /> e.g., user@example.com</li>
|
||||||
|
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /> Choose wisely.</li>
|
||||||
|
|
||||||
|
help_text is not displayed for hidden fields. It can be used for documentation
|
||||||
|
purposes, though.
|
||||||
|
>>> class UserRegistration(Form):
|
||||||
|
... username = CharField(max_length=10, help_text='e.g., user@example.com')
|
||||||
|
... password = CharField(widget=PasswordInput)
|
||||||
|
... next = CharField(widget=HiddenInput, initial='/', help_text='Redirect destination')
|
||||||
|
>>> p = UserRegistration(auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li>
|
||||||
|
<li>Password: <input type="password" name="password" /><input type="hidden" name="next" value="/" /></li>
|
||||||
|
|
||||||
# Forms with prefixes #########################################################
|
# Forms with prefixes #########################################################
|
||||||
|
|
||||||
Sometimes it's necessary to have multiple forms display on the same HTML page,
|
Sometimes it's necessary to have multiple forms display on the same HTML page,
|
||||||
@ -2311,6 +2756,57 @@ True
|
|||||||
>>> p.clean_data
|
>>> p.clean_data
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
||||||
|
|
||||||
|
# Forms with NullBooleanFields ################################################
|
||||||
|
|
||||||
|
NullBooleanField is a bit of a special case because its presentation (widget)
|
||||||
|
is different than its data. This is handled transparently, though.
|
||||||
|
|
||||||
|
>>> class Person(Form):
|
||||||
|
... name = CharField()
|
||||||
|
... is_cool = NullBooleanField()
|
||||||
|
>>> p = Person({'name': u'Joe'}, auto_id=False)
|
||||||
|
>>> print p['is_cool']
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1" selected="selected">Unknown</option>
|
||||||
|
<option value="2">Yes</option>
|
||||||
|
<option value="3">No</option>
|
||||||
|
</select>
|
||||||
|
>>> p = Person({'name': u'Joe', 'is_cool': u'1'}, auto_id=False)
|
||||||
|
>>> print p['is_cool']
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1" selected="selected">Unknown</option>
|
||||||
|
<option value="2">Yes</option>
|
||||||
|
<option value="3">No</option>
|
||||||
|
</select>
|
||||||
|
>>> p = Person({'name': u'Joe', 'is_cool': u'2'}, auto_id=False)
|
||||||
|
>>> print p['is_cool']
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1">Unknown</option>
|
||||||
|
<option value="2" selected="selected">Yes</option>
|
||||||
|
<option value="3">No</option>
|
||||||
|
</select>
|
||||||
|
>>> p = Person({'name': u'Joe', 'is_cool': u'3'}, auto_id=False)
|
||||||
|
>>> print p['is_cool']
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1">Unknown</option>
|
||||||
|
<option value="2">Yes</option>
|
||||||
|
<option value="3" selected="selected">No</option>
|
||||||
|
</select>
|
||||||
|
>>> p = Person({'name': u'Joe', 'is_cool': True}, auto_id=False)
|
||||||
|
>>> print p['is_cool']
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1">Unknown</option>
|
||||||
|
<option value="2" selected="selected">Yes</option>
|
||||||
|
<option value="3">No</option>
|
||||||
|
</select>
|
||||||
|
>>> p = Person({'name': u'Joe', 'is_cool': False}, auto_id=False)
|
||||||
|
>>> print p['is_cool']
|
||||||
|
<select name="is_cool">
|
||||||
|
<option value="1">Unknown</option>
|
||||||
|
<option value="2">Yes</option>
|
||||||
|
<option value="3" selected="selected">No</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
# Basic form processing in a view #############################################
|
# Basic form processing in a view #############################################
|
||||||
|
|
||||||
>>> from django.template import Template, Context
|
>>> from django.template import Template, Context
|
||||||
@ -2439,6 +2935,15 @@ field an "id" attribute.
|
|||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
The label_tag() method takes an optional attrs argument: a dictionary of HTML
|
||||||
|
attributes to add to the <label> tag.
|
||||||
|
>>> f = UserRegistration(auto_id='id_%s')
|
||||||
|
>>> for bf in f:
|
||||||
|
... print bf.label_tag(attrs={'class': 'pretty'})
|
||||||
|
<label for="id_username" class="pretty">Username</label>
|
||||||
|
<label for="id_password1" class="pretty">Password1</label>
|
||||||
|
<label for="id_password2" class="pretty">Password2</label>
|
||||||
|
|
||||||
To display the errors that aren't associated with a particular field -- e.g.,
|
To display the errors that aren't associated with a particular field -- e.g.,
|
||||||
the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the
|
the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the
|
||||||
template. If used on its own, it is displayed as a <ul> (or an empty string, if
|
template. If used on its own, it is displayed as a <ul> (or an empty string, if
|
||||||
|
Loading…
x
Reference in New Issue
Block a user