1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +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:
Jason Pellerin 2006-12-04 21:12:29 +00:00
parent 0162eac9e2
commit 55d7f6519c
17 changed files with 1063 additions and 163 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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()
# CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd) try:
content_length = int(self.environ.get('CONTENT_LENGTH', 0)) # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)
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()

View File

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

View File

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

View File

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

View File

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

View File

@ -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.errors: if bf.is_hidden:
output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors])) if bf.errors:
output.append('<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf)) new_errors = ErrorList(['(Hidden field %s) %s' % (name, e) for e in bf.errors])
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
View 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

View File

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

View File

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

View File

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

View File

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

View File

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