mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
[multi-db] Merged trunk to [4158]. Some tests still failing.
git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@4159 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
0162eac9e2
commit
55d7f6519c
1
AUTHORS
1
AUTHORS
@ -151,6 +151,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
SmileyChris <smileychris@gmail.com>
|
SmileyChris <smileychris@gmail.com>
|
||||||
sopel
|
sopel
|
||||||
Thomas Steinacher <tom@eggdrop.ch>
|
Thomas Steinacher <tom@eggdrop.ch>
|
||||||
|
nowell strite
|
||||||
Radek Švarz <http://www.svarz.cz/translate/>
|
Radek Švarz <http://www.svarz.cz/translate/>
|
||||||
Swaroop C H <http://www.swaroopch.info>
|
Swaroop C H <http://www.swaroopch.info>
|
||||||
Aaron Swartz <http://www.aaronsw.com/>
|
Aaron Swartz <http://www.aaronsw.com/>
|
||||||
|
@ -2,7 +2,7 @@ from django.conf.urls.defaults import *
|
|||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
# Example:
|
# Example:
|
||||||
# (r'^{{ project_name }}/', include('{{ project_name }}.apps.foo.urls.foo')),
|
# (r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),
|
||||||
|
|
||||||
# Uncomment this for admin:
|
# Uncomment this for admin:
|
||||||
# (r'^admin/', include('django.contrib.admin.urls')),
|
# (r'^admin/', include('django.contrib.admin.urls')),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemapindex/0.9">
|
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
{% for location in sitemaps %}<sitemap><loc>{{ location|escape }}</loc></sitemap>{% endfor %}
|
{% for location in sitemaps %}<sitemap><loc>{{ location|escape }}</loc></sitemap>{% endfor %}
|
||||||
</sitemapindex>
|
</sitemapindex>
|
||||||
|
@ -84,7 +84,11 @@ class BaseHandler(object):
|
|||||||
|
|
||||||
# Complain if the view returned None (a common error).
|
# Complain if the view returned None (a common error).
|
||||||
if response is None:
|
if response is None:
|
||||||
raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, callback.func_name)
|
try:
|
||||||
|
view_name = callback.func_name # If it's a function
|
||||||
|
except AttributeError:
|
||||||
|
view_name = callback.__class__.__name__ + '.__call__' # If it's a class
|
||||||
|
raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
except http.Http404, e:
|
except http.Http404, e:
|
||||||
|
@ -62,7 +62,7 @@ def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0):
|
|||||||
data in the body.
|
data in the body.
|
||||||
"""
|
"""
|
||||||
if not size:
|
if not size:
|
||||||
return copyfileobj(fsrc, fdst, length)
|
return
|
||||||
while size > 0:
|
while size > 0:
|
||||||
buf = fsrc.read(min(length, size))
|
buf = fsrc.read(min(length, size))
|
||||||
if not buf:
|
if not buf:
|
||||||
@ -157,8 +157,11 @@ class WSGIRequest(http.HttpRequest):
|
|||||||
return self._raw_post_data
|
return self._raw_post_data
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
buf = StringIO()
|
buf = StringIO()
|
||||||
|
try:
|
||||||
# CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)
|
# CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)
|
||||||
content_length = int(self.environ.get('CONTENT_LENGTH', 0))
|
content_length = int(self.environ.get('CONTENT_LENGTH', 0))
|
||||||
|
except ValueError: # if CONTENT_LENGTH was empty string or not an integer
|
||||||
|
content_length = 0
|
||||||
safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length)
|
safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length)
|
||||||
self._raw_post_data = buf.getvalue()
|
self._raw_post_data = buf.getvalue()
|
||||||
buf.close()
|
buf.close()
|
||||||
|
@ -457,9 +457,7 @@ class DateField(Field):
|
|||||||
|
|
||||||
def get_db_prep_save(self, value):
|
def get_db_prep_save(self, value):
|
||||||
# Casts dates into string format for entry into database.
|
# Casts dates into string format for entry into database.
|
||||||
if isinstance(value, datetime.datetime):
|
if value is not None:
|
||||||
value = value.date().strftime('%Y-%m-%d')
|
|
||||||
elif isinstance(value, datetime.date):
|
|
||||||
value = value.strftime('%Y-%m-%d')
|
value = value.strftime('%Y-%m-%d')
|
||||||
return Field.get_db_prep_save(self, value)
|
return Field.get_db_prep_save(self, value)
|
||||||
|
|
||||||
@ -489,7 +487,7 @@ class DateTimeField(DateField):
|
|||||||
|
|
||||||
def pre_save(self, model_instance, add):
|
def pre_save(self, model_instance, add):
|
||||||
value = super(DateField, self).pre_save(model_instance, add)
|
value = super(DateField, self).pre_save(model_instance, add)
|
||||||
if isinstance(value, datetime.datetime):
|
if value is not None:
|
||||||
# MySQL will throw a warning if microseconds are given, because it
|
# MySQL will throw a warning if microseconds are given, because it
|
||||||
# doesn't support microseconds.
|
# doesn't support microseconds.
|
||||||
settings = model_instance._default_manager.db.connection.settings
|
settings = model_instance._default_manager.db.connection.settings
|
||||||
@ -501,13 +499,6 @@ class DateTimeField(DateField):
|
|||||||
# Casts dates into string format for entry into database.
|
# Casts dates into string format for entry into database.
|
||||||
if value is not None:
|
if value is not None:
|
||||||
value = str(value)
|
value = str(value)
|
||||||
elif isinstance(value, datetime.date):
|
|
||||||
# MySQL will throw a warning if microseconds are given, because it
|
|
||||||
# doesn't support microseconds.
|
|
||||||
if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
|
|
||||||
value = datetime.datetime(value.year, value.month, value.day, microsecond=0)
|
|
||||||
value = str(value)
|
|
||||||
|
|
||||||
return Field.get_db_prep_save(self, value)
|
return Field.get_db_prep_save(self, value)
|
||||||
|
|
||||||
def get_db_prep_lookup(self, lookup_type, value):
|
def get_db_prep_lookup(self, lookup_type, value):
|
||||||
|
@ -208,7 +208,7 @@ class HttpResponse(object):
|
|||||||
if path is not None:
|
if path is not None:
|
||||||
self.cookies[key]['path'] = path
|
self.cookies[key]['path'] = path
|
||||||
if domain is not None:
|
if domain is not None:
|
||||||
self.cookies[key]['domain'] = path
|
self.cookies[key]['domain'] = domain
|
||||||
self.cookies[key]['expires'] = 0
|
self.cookies[key]['expires'] = 0
|
||||||
self.cookies[key]['max-age'] = 0
|
self.cookies[key]['max-age'] = 0
|
||||||
|
|
||||||
|
@ -14,15 +14,4 @@ from util import ValidationError
|
|||||||
from widgets import *
|
from widgets import *
|
||||||
from fields import *
|
from fields import *
|
||||||
from forms import Form
|
from forms import Form
|
||||||
|
from models import *
|
||||||
##########################
|
|
||||||
# DATABASE API SHORTCUTS #
|
|
||||||
##########################
|
|
||||||
|
|
||||||
def form_for_model(model):
|
|
||||||
"Returns a Form instance for the given Django model class."
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def form_for_fields(field_list):
|
|
||||||
"Returns a Form instance for the given list of Django database field instances."
|
|
||||||
raise NotImplementedError
|
|
||||||
|
@ -76,6 +76,8 @@ class IntegerField(Field):
|
|||||||
of int().
|
of int().
|
||||||
"""
|
"""
|
||||||
super(IntegerField, self).clean(value)
|
super(IntegerField, self).clean(value)
|
||||||
|
if not self.required and value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
try:
|
try:
|
||||||
return int(value)
|
return int(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
@ -170,6 +172,8 @@ class RegexField(Field):
|
|||||||
Field.clean(self, value)
|
Field.clean(self, 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'':
|
||||||
|
return value
|
||||||
if not self.regex.search(value):
|
if not self.regex.search(value):
|
||||||
raise ValidationError(self.error_message)
|
raise ValidationError(self.error_message)
|
||||||
return value
|
return value
|
||||||
@ -246,6 +250,8 @@ class ChoiceField(Field):
|
|||||||
value = Field.clean(self, value)
|
value = Field.clean(self, 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'':
|
||||||
|
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:
|
||||||
raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % value)
|
raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % value)
|
||||||
@ -259,10 +265,12 @@ class MultipleChoiceField(ChoiceField):
|
|||||||
"""
|
"""
|
||||||
Validates that the input is a list or tuple.
|
Validates that the input is a list or tuple.
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, (list, tuple)):
|
|
||||||
raise ValidationError(u'Enter a list of values.')
|
|
||||||
if self.required and not value:
|
if self.required and not value:
|
||||||
raise ValidationError(u'This field is required.')
|
raise ValidationError(u'This field is required.')
|
||||||
|
elif not self.required and not value:
|
||||||
|
return []
|
||||||
|
if not isinstance(value, (list, tuple)):
|
||||||
|
raise ValidationError(u'Enter a list of values.')
|
||||||
new_value = []
|
new_value = []
|
||||||
for val in value:
|
for val in value:
|
||||||
val = smart_unicode(val)
|
val = smart_unicode(val)
|
||||||
@ -277,6 +285,11 @@ class MultipleChoiceField(ChoiceField):
|
|||||||
class ComboField(Field):
|
class ComboField(Field):
|
||||||
def __init__(self, fields=(), required=True, widget=None):
|
def __init__(self, fields=(), required=True, widget=None):
|
||||||
Field.__init__(self, required, widget)
|
Field.__init__(self, required, widget)
|
||||||
|
# Set 'required' to False on the individual fields, because the
|
||||||
|
# required validation will be handled by ComboField, not by those
|
||||||
|
# individual fields.
|
||||||
|
for f in fields:
|
||||||
|
f.required = False
|
||||||
self.fields = fields
|
self.fields = fields
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
|
@ -3,8 +3,9 @@ Form classes
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
|
from django.utils.html import escape
|
||||||
from fields import Field
|
from fields import Field
|
||||||
from widgets import TextInput, Textarea
|
from widgets import TextInput, Textarea, HiddenInput
|
||||||
from util import ErrorDict, ErrorList, ValidationError
|
from util import ErrorDict, ErrorList, ValidationError
|
||||||
|
|
||||||
NON_FIELD_ERRORS = '__all__'
|
NON_FIELD_ERRORS = '__all__'
|
||||||
@ -36,6 +37,7 @@ class Form(object):
|
|||||||
__metaclass__ = DeclarativeFieldsMetaclass
|
__metaclass__ = DeclarativeFieldsMetaclass
|
||||||
|
|
||||||
def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
|
def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
|
||||||
|
self.ignore_errors = data is None
|
||||||
self.data = data or {}
|
self.data = data or {}
|
||||||
self.auto_id = auto_id
|
self.auto_id = auto_id
|
||||||
self.clean_data = None # Stores the data after clean() has been called.
|
self.clean_data = None # Stores the data after clean() has been called.
|
||||||
@ -56,60 +58,63 @@ class Form(object):
|
|||||||
raise KeyError('Key %r not found in Form' % name)
|
raise KeyError('Key %r not found in Form' % name)
|
||||||
return BoundField(self, field, name)
|
return BoundField(self, field, name)
|
||||||
|
|
||||||
def clean(self):
|
def _errors(self):
|
||||||
if self.__errors is None:
|
|
||||||
self.full_clean()
|
|
||||||
return self.clean_data
|
|
||||||
|
|
||||||
def errors(self):
|
|
||||||
"Returns an ErrorDict for self.data"
|
"Returns an ErrorDict for self.data"
|
||||||
if self.__errors is None:
|
if self.__errors is None:
|
||||||
self.full_clean()
|
self.full_clean()
|
||||||
return self.__errors
|
return self.__errors
|
||||||
|
errors = property(_errors)
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the form has no errors. Otherwise, False. This exists
|
Returns True if the form has no errors. Otherwise, False. If errors are
|
||||||
solely for convenience, so client code can use positive logic rather
|
being ignored, returns False.
|
||||||
than confusing negative logic ("if not form.errors()").
|
|
||||||
"""
|
"""
|
||||||
return not bool(self.errors())
|
return not self.ignore_errors and not bool(self.errors)
|
||||||
|
|
||||||
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 u'\n'.join(['<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
|
output = []
|
||||||
|
if self.errors.get(NON_FIELD_ERRORS):
|
||||||
|
# Errors not corresponding to a particular field are displayed at the top.
|
||||||
|
output.append(u'<tr><td colspan="2">%s</td></tr>' % self.non_field_errors())
|
||||||
|
for name, field in self.fields.items():
|
||||||
|
bf = BoundField(self, field, name)
|
||||||
|
if bf.is_hidden:
|
||||||
|
if bf.errors:
|
||||||
|
new_errors = ErrorList(['(Hidden field %s) %s' % (name, e) for e in bf.errors])
|
||||||
|
output.append(u'<tr><td colspan="2">%s</td></tr>' % new_errors)
|
||||||
|
output.append(str(bf))
|
||||||
|
else:
|
||||||
|
if bf.errors:
|
||||||
|
output.append(u'<tr><td colspan="2">%s</td></tr>' % bf.errors)
|
||||||
|
output.append(u'<tr><td>%s</td><td>%s</td></tr>' % (bf.label_tag(escape(bf.verbose_name+':')), bf))
|
||||||
|
return u'\n'.join(output)
|
||||||
|
|
||||||
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 u'\n'.join(['<li>%s: %s</li>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
|
|
||||||
|
|
||||||
def as_table_with_errors(self):
|
|
||||||
"Returns this form rendered as HTML <tr>s, with errors."
|
|
||||||
output = []
|
output = []
|
||||||
if self.errors().get(NON_FIELD_ERRORS):
|
if self.errors.get(NON_FIELD_ERRORS):
|
||||||
# Errors not corresponding to a particular field are displayed at the top.
|
# Errors not corresponding to a particular field are displayed at the top.
|
||||||
output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in self.errors()[NON_FIELD_ERRORS]]))
|
output.append(u'<li>%s</li>' % self.non_field_errors())
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
bf = BoundField(self, field, name)
|
bf = BoundField(self, field, name)
|
||||||
|
if bf.is_hidden:
|
||||||
if bf.errors:
|
if bf.errors:
|
||||||
output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors]))
|
new_errors = ErrorList(['(Hidden field %s) %s' % (name, e) for e in bf.errors])
|
||||||
output.append('<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf))
|
output.append(u'<li>%s</li>' % new_errors)
|
||||||
|
output.append(str(bf))
|
||||||
|
else:
|
||||||
|
output.append(u'<li>%s%s %s</li>' % (bf.errors, bf.label_tag(escape(bf.verbose_name+':')), bf))
|
||||||
return u'\n'.join(output)
|
return u'\n'.join(output)
|
||||||
|
|
||||||
def as_ul_with_errors(self):
|
def non_field_errors(self):
|
||||||
"Returns this form rendered as HTML <li>s, with errors."
|
"""
|
||||||
output = []
|
Returns an ErrorList of errors that aren't associated with a particular
|
||||||
if self.errors().get(NON_FIELD_ERRORS):
|
field -- i.e., from Form.clean(). Returns an empty ErrorList if there
|
||||||
# Errors not corresponding to a particular field are displayed at the top.
|
are none.
|
||||||
output.append('<li><ul>%s</ul></li>' % '\n'.join(['<li>%s</li>' % e for e in self.errors()[NON_FIELD_ERRORS]]))
|
"""
|
||||||
for name, field in self.fields.items():
|
return self.errors.get(NON_FIELD_ERRORS, ErrorList())
|
||||||
bf = BoundField(self, field, name)
|
|
||||||
line = '<li>'
|
|
||||||
if bf.errors:
|
|
||||||
line += '<ul>%s</ul>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors])
|
|
||||||
line += '%s: %s</li>' % (pretty_name(name), bf)
|
|
||||||
output.append(line)
|
|
||||||
return u'\n'.join(output)
|
|
||||||
|
|
||||||
def full_clean(self):
|
def full_clean(self):
|
||||||
"""
|
"""
|
||||||
@ -117,8 +122,14 @@ class Form(object):
|
|||||||
"""
|
"""
|
||||||
self.clean_data = {}
|
self.clean_data = {}
|
||||||
errors = ErrorDict()
|
errors = ErrorDict()
|
||||||
|
if self.ignore_errors: # Stop further processing.
|
||||||
|
self.__errors = errors
|
||||||
|
return
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
value = self.data.get(name, None)
|
# value_from_datadict() gets the data from the dictionary.
|
||||||
|
# Each widget type knows how to retrieve its own data, because some
|
||||||
|
# widgets split data over several HTML fields.
|
||||||
|
value = field.widget.value_from_datadict(self.data, name)
|
||||||
try:
|
try:
|
||||||
value = field.clean(value)
|
value = field.clean(value)
|
||||||
self.clean_data[name] = value
|
self.clean_data[name] = value
|
||||||
@ -138,7 +149,9 @@ class Form(object):
|
|||||||
def clean(self):
|
def clean(self):
|
||||||
"""
|
"""
|
||||||
Hook for doing any extra form-wide cleaning after Field.clean() been
|
Hook for doing any extra form-wide cleaning after Field.clean() been
|
||||||
called on every field.
|
called on every field. Any ValidationError raised by this method will
|
||||||
|
not be associated with a particular field; it will have a special-case
|
||||||
|
association with the field named '__all__'.
|
||||||
"""
|
"""
|
||||||
return self.clean_data
|
return self.clean_data
|
||||||
|
|
||||||
@ -153,7 +166,13 @@ class BoundField(object):
|
|||||||
"Renders this field as an HTML widget."
|
"Renders this field as an HTML widget."
|
||||||
# Use the 'widget' attribute on the field to determine which type
|
# Use the 'widget' attribute on the field to determine which type
|
||||||
# of HTML widget to use.
|
# of HTML widget to use.
|
||||||
return self.as_widget(self._field.widget)
|
value = self.as_widget(self._field.widget)
|
||||||
|
if not isinstance(value, basestring):
|
||||||
|
# Some Widget render() methods -- notably RadioSelect -- return a
|
||||||
|
# "special" object rather than a string. Call the __str__() on that
|
||||||
|
# object to get its rendered value.
|
||||||
|
value = value.__str__()
|
||||||
|
return value
|
||||||
|
|
||||||
def _errors(self):
|
def _errors(self):
|
||||||
"""
|
"""
|
||||||
@ -161,7 +180,7 @@ class BoundField(object):
|
|||||||
if there are none.
|
if there are none.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self._form.errors()[self._name]
|
return self._form.errors[self._name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return ErrorList()
|
return ErrorList()
|
||||||
errors = property(_errors)
|
errors = property(_errors)
|
||||||
@ -169,9 +188,9 @@ class BoundField(object):
|
|||||||
def as_widget(self, widget, attrs=None):
|
def as_widget(self, widget, attrs=None):
|
||||||
attrs = attrs or {}
|
attrs = attrs or {}
|
||||||
auto_id = self.auto_id
|
auto_id = self.auto_id
|
||||||
if not attrs.has_key('id') and not widget.attrs.has_key('id') and auto_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
|
||||||
return widget.render(self._name, self._form.data.get(self._name, None), attrs=attrs)
|
return widget.render(self._name, self.data, attrs=attrs)
|
||||||
|
|
||||||
def as_text(self, attrs=None):
|
def as_text(self, attrs=None):
|
||||||
"""
|
"""
|
||||||
@ -183,6 +202,39 @@ class BoundField(object):
|
|||||||
"Returns a string of HTML for representing this as a <textarea>."
|
"Returns a string of HTML for representing this as a <textarea>."
|
||||||
return self.as_widget(Textarea(), attrs)
|
return self.as_widget(Textarea(), attrs)
|
||||||
|
|
||||||
|
def as_hidden(self, attrs=None):
|
||||||
|
"""
|
||||||
|
Returns a string of HTML for representing this as an <input type="hidden">.
|
||||||
|
"""
|
||||||
|
return self.as_widget(HiddenInput(), attrs)
|
||||||
|
|
||||||
|
def _data(self):
|
||||||
|
"Returns the data for this BoundField, or None if it wasn't given."
|
||||||
|
return self._form.data.get(self._name, None)
|
||||||
|
data = property(_data)
|
||||||
|
|
||||||
|
def _verbose_name(self):
|
||||||
|
return pretty_name(self._name)
|
||||||
|
verbose_name = property(_verbose_name)
|
||||||
|
|
||||||
|
def label_tag(self, contents=None):
|
||||||
|
"""
|
||||||
|
Wraps the given contents in a <label>, if the field has an ID attribute.
|
||||||
|
Does not HTML-escape the contents. If contents aren't given, uses the
|
||||||
|
field's HTML-escaped verbose_name.
|
||||||
|
"""
|
||||||
|
contents = contents or escape(self.verbose_name)
|
||||||
|
widget = self._field.widget
|
||||||
|
id_ = widget.attrs.get('id') or self.auto_id
|
||||||
|
if id_:
|
||||||
|
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents)
|
||||||
|
return contents
|
||||||
|
|
||||||
|
def _is_hidden(self):
|
||||||
|
"Returns True if this BoundField's widget is hidden."
|
||||||
|
return self._field.widget.is_hidden
|
||||||
|
is_hidden = property(_is_hidden)
|
||||||
|
|
||||||
def _auto_id(self):
|
def _auto_id(self):
|
||||||
"""
|
"""
|
||||||
Calculates and returns the ID attribute for this BoundField, if the
|
Calculates and returns the ID attribute for this BoundField, if the
|
||||||
|
13
django/newforms/models.py
Normal file
13
django/newforms/models.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
"""
|
||||||
|
Helper functions for creating Forms from Django models and database field objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__all__ = ('form_for_model', 'form_for_fields')
|
||||||
|
|
||||||
|
def form_for_model(model):
|
||||||
|
"Returns a Form instance for the given Django model class."
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def form_for_fields(field_list):
|
||||||
|
"Returns a Form instance for the given list of Django database field instances."
|
||||||
|
raise NotImplementedError
|
@ -5,7 +5,7 @@ HTML Widget classes
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
|
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
|
||||||
'Textarea', 'CheckboxInput',
|
'Textarea', 'CheckboxInput',
|
||||||
'Select', 'SelectMultiple', 'RadioSelect',
|
'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
|
||||||
)
|
)
|
||||||
|
|
||||||
from util import smart_unicode
|
from util import smart_unicode
|
||||||
@ -23,6 +23,8 @@ flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs
|
|||||||
|
|
||||||
class Widget(object):
|
class Widget(object):
|
||||||
requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
|
requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
|
||||||
|
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
||||||
|
|
||||||
def __init__(self, attrs=None):
|
def __init__(self, attrs=None):
|
||||||
self.attrs = attrs or {}
|
self.attrs = attrs or {}
|
||||||
|
|
||||||
@ -30,11 +32,32 @@ class Widget(object):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||||
|
"Helper function for building an attribute dictionary."
|
||||||
attrs = dict(self.attrs, **kwargs)
|
attrs = dict(self.attrs, **kwargs)
|
||||||
if extra_attrs:
|
if extra_attrs:
|
||||||
attrs.update(extra_attrs)
|
attrs.update(extra_attrs)
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, name):
|
||||||
|
"""
|
||||||
|
Given a dictionary of data and this widget's name, returns the value
|
||||||
|
of this widget. Returns None if it's not provided.
|
||||||
|
"""
|
||||||
|
return data.get(name, None)
|
||||||
|
|
||||||
|
def id_for_label(self, id_):
|
||||||
|
"""
|
||||||
|
Returns the HTML ID attribute of this Widget for use by a <label>,
|
||||||
|
given the ID of the field. Returns None if no ID is available.
|
||||||
|
|
||||||
|
This hook is necessary because some widgets have multiple HTML
|
||||||
|
elements and, thus, multiple IDs. In that case, this method should
|
||||||
|
return an ID value that corresponds to the first ID in the widget's
|
||||||
|
tags.
|
||||||
|
"""
|
||||||
|
return id_
|
||||||
|
id_for_label = classmethod(id_for_label)
|
||||||
|
|
||||||
class Input(Widget):
|
class Input(Widget):
|
||||||
"""
|
"""
|
||||||
Base class for all <input> widgets (except type='checkbox' and
|
Base class for all <input> widgets (except type='checkbox' and
|
||||||
@ -55,6 +78,7 @@ class PasswordInput(Input):
|
|||||||
|
|
||||||
class HiddenInput(Input):
|
class HiddenInput(Input):
|
||||||
input_type = 'hidden'
|
input_type = 'hidden'
|
||||||
|
is_hidden = True
|
||||||
|
|
||||||
class FileInput(Input):
|
class FileInput(Input):
|
||||||
input_type = 'file'
|
input_type = 'file'
|
||||||
@ -67,9 +91,22 @@ class Textarea(Widget):
|
|||||||
return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
|
return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
|
||||||
|
|
||||||
class CheckboxInput(Widget):
|
class CheckboxInput(Widget):
|
||||||
|
def __init__(self, attrs=None, check_test=bool):
|
||||||
|
# check_test is a callable that takes a value and returns True
|
||||||
|
# if the checkbox should be checked for that value.
|
||||||
|
self.attrs = attrs or {}
|
||||||
|
self.check_test = check_test
|
||||||
|
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
|
final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
|
||||||
if value: final_attrs['checked'] = 'checked'
|
try:
|
||||||
|
result = self.check_test(value)
|
||||||
|
except: # Silently catch exceptions
|
||||||
|
result = False
|
||||||
|
if result:
|
||||||
|
final_attrs['checked'] = 'checked'
|
||||||
|
if value not in ('', True, False, None):
|
||||||
|
final_attrs['value'] = smart_unicode(value) # Only add the 'value' attribute if a value is non-empty.
|
||||||
return u'<input%s />' % flatatt(final_attrs)
|
return u'<input%s />' % flatatt(final_attrs)
|
||||||
|
|
||||||
class Select(Widget):
|
class Select(Widget):
|
||||||
@ -111,10 +148,11 @@ class SelectMultiple(Widget):
|
|||||||
|
|
||||||
class RadioInput(object):
|
class RadioInput(object):
|
||||||
"An object used by RadioFieldRenderer that represents a single <input type='radio'>."
|
"An object used by RadioFieldRenderer that represents a single <input type='radio'>."
|
||||||
def __init__(self, name, value, attrs, choice):
|
def __init__(self, name, value, attrs, choice, index):
|
||||||
self.name, self.value = name, value
|
self.name, self.value = name, value
|
||||||
self.attrs = attrs or {}
|
self.attrs = attrs
|
||||||
self.choice_value, self.choice_label = choice
|
self.choice_value, self.choice_label = choice
|
||||||
|
self.index = index
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
||||||
@ -123,6 +161,8 @@ class RadioInput(object):
|
|||||||
return self.value == smart_unicode(self.choice_value)
|
return self.value == smart_unicode(self.choice_value)
|
||||||
|
|
||||||
def tag(self):
|
def tag(self):
|
||||||
|
if self.attrs.has_key('id'):
|
||||||
|
self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
|
||||||
final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
|
final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
|
||||||
if self.is_checked():
|
if self.is_checked():
|
||||||
final_attrs['checked'] = 'checked'
|
final_attrs['checked'] = 'checked'
|
||||||
@ -135,8 +175,8 @@ class RadioFieldRenderer(object):
|
|||||||
self.choices = choices
|
self.choices = choices
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for choice in self.choices:
|
for i, choice in enumerate(self.choices):
|
||||||
yield RadioInput(self.name, self.value, self.attrs, choice)
|
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"Outputs a <ul> for this set of radio fields."
|
"Outputs a <ul> for this set of radio fields."
|
||||||
@ -147,7 +187,36 @@ class RadioSelect(Select):
|
|||||||
"Returns a RadioFieldRenderer instance rather than a Unicode string."
|
"Returns a RadioFieldRenderer instance rather than a Unicode string."
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
str_value = smart_unicode(value) # Normalize to string.
|
str_value = smart_unicode(value) # Normalize to string.
|
||||||
|
attrs = attrs or {}
|
||||||
return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
|
return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
|
||||||
|
|
||||||
class CheckboxSelectMultiple(Widget):
|
def id_for_label(self, id_):
|
||||||
pass
|
# RadioSelect is represented by multiple <input type="radio"> fields,
|
||||||
|
# each of which has a distinct ID. The IDs are made distinct by a "_X"
|
||||||
|
# suffix, where X is the zero-based index of the radio field. Thus,
|
||||||
|
# the label for a RadioSelect should reference the first one ('_0').
|
||||||
|
if id_:
|
||||||
|
id_ += '_0'
|
||||||
|
return id_
|
||||||
|
id_for_label = classmethod(id_for_label)
|
||||||
|
|
||||||
|
class CheckboxSelectMultiple(SelectMultiple):
|
||||||
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
|
if value is None: value = []
|
||||||
|
final_attrs = self.build_attrs(attrs, name=name)
|
||||||
|
output = [u'<ul>']
|
||||||
|
str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
|
||||||
|
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
|
||||||
|
for option_value, option_label in chain(self.choices, choices):
|
||||||
|
option_value = smart_unicode(option_value)
|
||||||
|
rendered_cb = cb.render(name, option_value)
|
||||||
|
output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label))))
|
||||||
|
output.append(u'</ul>')
|
||||||
|
return u'\n'.join(output)
|
||||||
|
|
||||||
|
def id_for_label(self, id_):
|
||||||
|
# See the comment for RadioSelect.id_for_label()
|
||||||
|
if id_:
|
||||||
|
id_ += '_0'
|
||||||
|
return id_
|
||||||
|
id_for_label = classmethod(id_for_label)
|
||||||
|
@ -84,7 +84,7 @@ def object_detail(request, queryset, object_id=None, slug=None,
|
|||||||
context_processors=None, template_object_name='object',
|
context_processors=None, template_object_name='object',
|
||||||
mimetype=None):
|
mimetype=None):
|
||||||
"""
|
"""
|
||||||
Generic list of objects.
|
Generic detail of an object.
|
||||||
|
|
||||||
Templates: ``<app_label>/<model_name>_detail.html``
|
Templates: ``<app_label>/<model_name>_detail.html``
|
||||||
Context:
|
Context:
|
||||||
|
@ -141,7 +141,7 @@ Do this after you've verified that the test cookie worked.
|
|||||||
Here's a typical usage example::
|
Here's a typical usage example::
|
||||||
|
|
||||||
def login(request):
|
def login(request):
|
||||||
if request.POST:
|
if request.method == 'POST':
|
||||||
if request.session.test_cookie_worked():
|
if request.session.test_cookie_worked():
|
||||||
request.session.delete_test_cookie()
|
request.session.delete_test_cookie()
|
||||||
return HttpResponse("You're logged in.")
|
return HttpResponse("You're logged in.")
|
||||||
|
@ -133,7 +133,7 @@ together, picking the test system to match the type of tests you need to
|
|||||||
write.
|
write.
|
||||||
|
|
||||||
For developers new to testing, however, this choice can seem
|
For developers new to testing, however, this choice can seem
|
||||||
confusing, so here are a few key differences to help you decide weather
|
confusing, so here are a few key differences to help you decide whether
|
||||||
doctests or unit tests are right for you.
|
doctests or unit tests are right for you.
|
||||||
|
|
||||||
If you've been using Python for a while, ``doctest`` will probably feel more
|
If you've been using Python for a while, ``doctest`` will probably feel more
|
||||||
|
10
setup.py
10
setup.py
@ -11,13 +11,17 @@ for scheme in INSTALL_SCHEMES.values():
|
|||||||
# Compile the list of packages available, because distutils doesn't have
|
# Compile the list of packages available, because distutils doesn't have
|
||||||
# an easy way to do this.
|
# an easy way to do this.
|
||||||
packages, data_files = [], []
|
packages, data_files = [], []
|
||||||
root_dir = os.path.join(os.path.dirname(__file__), 'django')
|
root_dir = os.path.dirname(__file__)
|
||||||
for dirpath, dirnames, filenames in os.walk(root_dir):
|
len_root_dir = len(root_dir)
|
||||||
|
django_dir = os.path.join(root_dir, 'django')
|
||||||
|
|
||||||
|
for dirpath, dirnames, filenames in os.walk(django_dir):
|
||||||
# Ignore dirnames that start with '.'
|
# Ignore dirnames that start with '.'
|
||||||
for i, dirname in enumerate(dirnames):
|
for i, dirname in enumerate(dirnames):
|
||||||
if dirname.startswith('.'): del dirnames[i]
|
if dirname.startswith('.'): del dirnames[i]
|
||||||
if '__init__.py' in filenames:
|
if '__init__.py' in filenames:
|
||||||
packages.append(dirpath.replace('/', '.'))
|
package = dirpath[len_root_dir:].lstrip('/').replace('/', '.')
|
||||||
|
packages.append(package)
|
||||||
else:
|
else:
|
||||||
data_files.append((dirpath, [os.path.join(dirpath, f) for f in filenames]))
|
data_files.append((dirpath, [os.path.join(dirpath, f) for f in filenames]))
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user