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>
sopel
Thomas Steinacher <tom@eggdrop.ch>
nowell strite
Radek Švarz <http://www.svarz.cz/translate/>
Swaroop C H <http://www.swaroopch.info>
Aaron Swartz <http://www.aaronsw.com/>

View File

@ -2,7 +2,7 @@ from django.conf.urls.defaults import *
urlpatterns = patterns('',
# Example:
# (r'^{{ project_name }}/', include('{{ project_name }}.apps.foo.urls.foo')),
# (r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),
# Uncomment this for admin:
# (r'^admin/', include('django.contrib.admin.urls')),

View File

@ -1,4 +1,4 @@
<?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 %}
</sitemapindex>

View File

@ -84,7 +84,11 @@ class BaseHandler(object):
# Complain if the view returned None (a common error).
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
except http.Http404, e:

View File

@ -62,7 +62,7 @@ def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0):
data in the body.
"""
if not size:
return copyfileobj(fsrc, fdst, length)
return
while size > 0:
buf = fsrc.read(min(length, size))
if not buf:
@ -157,8 +157,11 @@ class WSGIRequest(http.HttpRequest):
return self._raw_post_data
except AttributeError:
buf = StringIO()
# CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)
content_length = int(self.environ.get('CONTENT_LENGTH', 0))
try:
# 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)
self._raw_post_data = buf.getvalue()
buf.close()

View File

@ -457,9 +457,7 @@ class DateField(Field):
def get_db_prep_save(self, value):
# Casts dates into string format for entry into database.
if isinstance(value, datetime.datetime):
value = value.date().strftime('%Y-%m-%d')
elif isinstance(value, datetime.date):
if value is not None:
value = value.strftime('%Y-%m-%d')
return Field.get_db_prep_save(self, value)
@ -489,7 +487,7 @@ class DateTimeField(DateField):
def pre_save(self, 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
# doesn't support microseconds.
settings = model_instance._default_manager.db.connection.settings
@ -501,13 +499,6 @@ class DateTimeField(DateField):
# Casts dates into string format for entry into database.
if value is not None:
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)
def get_db_prep_lookup(self, lookup_type, value):

View File

@ -208,7 +208,7 @@ class HttpResponse(object):
if path is not None:
self.cookies[key]['path'] = path
if domain is not None:
self.cookies[key]['domain'] = path
self.cookies[key]['domain'] = domain
self.cookies[key]['expires'] = 0
self.cookies[key]['max-age'] = 0

View File

@ -14,15 +14,4 @@ from util import ValidationError
from widgets import *
from fields import *
from forms import Form
##########################
# 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
from models import *

View File

@ -76,6 +76,8 @@ class IntegerField(Field):
of int().
"""
super(IntegerField, self).clean(value)
if not self.required and value in EMPTY_VALUES:
return u''
try:
return int(value)
except (ValueError, TypeError):
@ -170,6 +172,8 @@ class RegexField(Field):
Field.clean(self, value)
if value in EMPTY_VALUES: value = u''
value = smart_unicode(value)
if not self.required and value == u'':
return value
if not self.regex.search(value):
raise ValidationError(self.error_message)
return value
@ -246,6 +250,8 @@ class ChoiceField(Field):
value = Field.clean(self, value)
if value in EMPTY_VALUES: value = u''
value = smart_unicode(value)
if not self.required and value == u'':
return value
valid_values = set([str(k) for k, v in self.choices])
if value not in valid_values:
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.
"""
if not isinstance(value, (list, tuple)):
raise ValidationError(u'Enter a list of values.')
if self.required and not value:
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 = []
for val in value:
val = smart_unicode(val)
@ -277,6 +285,11 @@ class MultipleChoiceField(ChoiceField):
class ComboField(Field):
def __init__(self, fields=(), required=True, widget=None):
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
def clean(self, value):

View File

@ -3,8 +3,9 @@ Form classes
"""
from django.utils.datastructures import SortedDict
from django.utils.html import escape
from fields import Field
from widgets import TextInput, Textarea
from widgets import TextInput, Textarea, HiddenInput
from util import ErrorDict, ErrorList, ValidationError
NON_FIELD_ERRORS = '__all__'
@ -36,6 +37,7 @@ class Form(object):
__metaclass__ = DeclarativeFieldsMetaclass
def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
self.ignore_errors = data is None
self.data = data or {}
self.auto_id = auto_id
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)
return BoundField(self, field, name)
def clean(self):
if self.__errors is None:
self.full_clean()
return self.clean_data
def errors(self):
def _errors(self):
"Returns an ErrorDict for self.data"
if self.__errors is None:
self.full_clean()
return self.__errors
errors = property(_errors)
def is_valid(self):
"""
Returns True if the form has no errors. Otherwise, False. This exists
solely for convenience, so client code can use positive logic rather
than confusing negative logic ("if not form.errors()").
Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False.
"""
return not bool(self.errors())
return not self.ignore_errors and not bool(self.errors)
def as_table(self):
"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):
"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 = []
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.
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():
bf = BoundField(self, field, name)
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]))
output.append('<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf))
if bf.is_hidden:
if bf.errors:
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)
def as_ul_with_errors(self):
"Returns this form rendered as HTML <li>s, with errors."
output = []
if self.errors().get(NON_FIELD_ERRORS):
# Errors not corresponding to a particular field are displayed at the top.
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():
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 non_field_errors(self):
"""
Returns an ErrorList of errors that aren't associated with a particular
field -- i.e., from Form.clean(). Returns an empty ErrorList if there
are none.
"""
return self.errors.get(NON_FIELD_ERRORS, ErrorList())
def full_clean(self):
"""
@ -117,8 +122,14 @@ class Form(object):
"""
self.clean_data = {}
errors = ErrorDict()
if self.ignore_errors: # Stop further processing.
self.__errors = errors
return
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:
value = field.clean(value)
self.clean_data[name] = value
@ -138,7 +149,9 @@ class Form(object):
def clean(self):
"""
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
@ -153,7 +166,13 @@ class BoundField(object):
"Renders this field as an HTML widget."
# Use the 'widget' attribute on the field to determine which type
# 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):
"""
@ -161,7 +180,7 @@ class BoundField(object):
if there are none.
"""
try:
return self._form.errors()[self._name]
return self._form.errors[self._name]
except KeyError:
return ErrorList()
errors = property(_errors)
@ -169,9 +188,9 @@ class BoundField(object):
def as_widget(self, widget, attrs=None):
attrs = attrs or {}
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
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):
"""
@ -183,6 +202,39 @@ class BoundField(object):
"Returns a string of HTML for representing this as a <textarea>."
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):
"""
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__ = (
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
'Textarea', 'CheckboxInput',
'Select', 'SelectMultiple', 'RadioSelect',
'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
)
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):
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):
self.attrs = attrs or {}
@ -30,11 +32,32 @@ class Widget(object):
raise NotImplementedError
def build_attrs(self, extra_attrs=None, **kwargs):
"Helper function for building an attribute dictionary."
attrs = dict(self.attrs, **kwargs)
if extra_attrs:
attrs.update(extra_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):
"""
Base class for all <input> widgets (except type='checkbox' and
@ -55,6 +78,7 @@ class PasswordInput(Input):
class HiddenInput(Input):
input_type = 'hidden'
is_hidden = True
class FileInput(Input):
input_type = 'file'
@ -67,9 +91,22 @@ class Textarea(Widget):
return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
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):
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)
class Select(Widget):
@ -111,10 +148,11 @@ class SelectMultiple(Widget):
class RadioInput(object):
"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.attrs = attrs or {}
self.attrs = attrs
self.choice_value, self.choice_label = choice
self.index = index
def __str__(self):
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)
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)
if self.is_checked():
final_attrs['checked'] = 'checked'
@ -135,8 +175,8 @@ class RadioFieldRenderer(object):
self.choices = choices
def __iter__(self):
for choice in self.choices:
yield RadioInput(self.name, self.value, self.attrs, choice)
for i, choice in enumerate(self.choices):
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
def __str__(self):
"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."
if value is None: value = ''
str_value = smart_unicode(value) # Normalize to string.
attrs = attrs or {}
return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
class CheckboxSelectMultiple(Widget):
pass
def id_for_label(self, id_):
# 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',
mimetype=None):
"""
Generic list of objects.
Generic detail of an object.
Templates: ``<app_label>/<model_name>_detail.html``
Context:

View File

@ -141,7 +141,7 @@ Do this after you've verified that the test cookie worked.
Here's a typical usage example::
def login(request):
if request.POST:
if request.method == 'POST':
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
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.
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.
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
# an easy way to do this.
packages, data_files = [], []
root_dir = os.path.join(os.path.dirname(__file__), 'django')
for dirpath, dirnames, filenames in os.walk(root_dir):
root_dir = os.path.dirname(__file__)
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 '.'
for i, dirname in enumerate(dirnames):
if dirname.startswith('.'): del dirnames[i]
if '__init__.py' in filenames:
packages.append(dirpath.replace('/', '.'))
package = dirpath[len_root_dir:].lstrip('/').replace('/', '.')
packages.append(package)
else:
data_files.append((dirpath, [os.path.join(dirpath, f) for f in filenames]))

File diff suppressed because it is too large Load Diff