Changeset 4159 for django/branches/multiple-db-support
- Timestamp:
- 12/04/06 15:12:29 (2 years ago)
- Files:
-
- django/branches/multiple-db-support/AUTHORS (modified) (1 diff)
- django/branches/multiple-db-support/django/conf/project_template/urls.py (modified) (1 diff)
- django/branches/multiple-db-support/django/contrib/sitemaps/templates/sitemap_index.xml (modified) (1 diff)
- django/branches/multiple-db-support/django/core/handlers/base.py (modified) (1 diff)
- django/branches/multiple-db-support/django/core/handlers/wsgi.py (modified) (2 diffs)
- django/branches/multiple-db-support/django/db/models/fields/__init__.py (modified) (3 diffs)
- django/branches/multiple-db-support/django/http/__init__.py (modified) (1 diff)
- django/branches/multiple-db-support/django/newforms/fields.py (modified) (5 diffs)
- django/branches/multiple-db-support/django/newforms/forms.py (modified) (9 diffs)
- django/branches/multiple-db-support/django/newforms/__init__.py (modified) (1 diff)
- django/branches/multiple-db-support/django/newforms/models.py (copied) (copied from django/trunk/django/newforms/models.py)
- django/branches/multiple-db-support/django/newforms/widgets.py (modified) (9 diffs)
- django/branches/multiple-db-support/django/views/generic/list_detail.py (modified) (1 diff)
- django/branches/multiple-db-support/docs/sessions.txt (modified) (1 diff)
- django/branches/multiple-db-support/docs/testing.txt (modified) (1 diff)
- django/branches/multiple-db-support/setup.py (modified) (1 diff)
- django/branches/multiple-db-support/tests/regressiontests/forms/tests.py (modified) (33 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/multiple-db-support/AUTHORS
r4158 r4159 152 152 sopel 153 153 Thomas Steinacher <tom@eggdrop.ch> 154 nowell strite 154 155 Radek Švarz <http://www.svarz.cz/translate/> 155 156 Swaroop C H <http://www.swaroopch.info> django/branches/multiple-db-support/django/conf/project_template/urls.py
r2809 r4159 3 3 urlpatterns = patterns('', 4 4 # Example: 5 # (r'^{{ project_name }}/', include('{{ project_name }}. apps.foo.urls.foo')),5 # (r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')), 6 6 7 7 # Uncomment this for admin: django/branches/multiple-db-support/django/contrib/sitemaps/templates/sitemap_index.xml
r4158 r4159 1 1 <?xml version="1.0" encoding="UTF-8"?> 2 <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap index/0.9">2 <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> 3 3 {% for location in sitemaps %}<sitemap><loc>{{ location|escape }}</loc></sitemap>{% endfor %} 4 4 </sitemapindex> django/branches/multiple-db-support/django/core/handlers/base.py
r4156 r4159 85 85 # Complain if the view returned None (a common error). 86 86 if response is None: 87 raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, callback.func_name) 87 try: 88 view_name = callback.func_name # If it's a function 89 except AttributeError: 90 view_name = callback.__class__.__name__ + '.__call__' # If it's a class 91 raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name) 88 92 89 93 return response django/branches/multiple-db-support/django/core/handlers/wsgi.py
r4158 r4159 63 63 """ 64 64 if not size: 65 return copyfileobj(fsrc, fdst, length)65 return 66 66 while size > 0: 67 67 buf = fsrc.read(min(length, size)) … … 158 158 except AttributeError: 159 159 buf = StringIO() 160 # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd) 161 content_length = int(self.environ.get('CONTENT_LENGTH', 0)) 160 try: 161 # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd) 162 content_length = int(self.environ.get('CONTENT_LENGTH', 0)) 163 except ValueError: # if CONTENT_LENGTH was empty string or not an integer 164 content_length = 0 162 165 safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length) 163 166 self._raw_post_data = buf.getvalue() django/branches/multiple-db-support/django/db/models/fields/__init__.py
r4157 r4159 458 458 def get_db_prep_save(self, value): 459 459 # Casts dates into string format for entry into database. 460 if isinstance(value, datetime.datetime): 461 value = value.date().strftime('%Y-%m-%d') 462 elif isinstance(value, datetime.date): 460 if value is not None: 463 461 value = value.strftime('%Y-%m-%d') 464 462 return Field.get_db_prep_save(self, value) … … 490 488 def pre_save(self, model_instance, add): 491 489 value = super(DateField, self).pre_save(model_instance, add) 492 if isinstance(value, datetime.datetime):490 if value is not None: 493 491 # MySQL will throw a warning if microseconds are given, because it 494 492 # doesn't support microseconds. … … 502 500 if value is not None: 503 501 value = str(value) 504 elif isinstance(value, datetime.date):505 # MySQL will throw a warning if microseconds are given, because it506 # doesn't support microseconds.507 if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):508 value = datetime.datetime(value.year, value.month, value.day, microsecond=0)509 value = str(value)510 511 502 return Field.get_db_prep_save(self, value) 512 503 django/branches/multiple-db-support/django/http/__init__.py
r4139 r4159 209 209 self.cookies[key]['path'] = path 210 210 if domain is not None: 211 self.cookies[key]['domain'] = path211 self.cookies[key]['domain'] = domain 212 212 self.cookies[key]['expires'] = 0 213 213 self.cookies[key]['max-age'] = 0 django/branches/multiple-db-support/django/newforms/fields.py
r4158 r4159 77 77 """ 78 78 super(IntegerField, self).clean(value) 79 if not self.required and value in EMPTY_VALUES: 80 return u'' 79 81 try: 80 82 return int(value) … … 171 173 if value in EMPTY_VALUES: value = u'' 172 174 value = smart_unicode(value) 175 if not self.required and value == u'': 176 return value 173 177 if not self.regex.search(value): 174 178 raise ValidationError(self.error_message) … … 247 251 if value in EMPTY_VALUES: value = u'' 248 252 value = smart_unicode(value) 253 if not self.required and value == u'': 254 return value 249 255 valid_values = set([str(k) for k, v in self.choices]) 250 256 if value not in valid_values: … … 260 266 Validates that the input is a list or tuple. 261 267 """ 268 if self.required and not value: 269 raise ValidationError(u'This field is required.') 270 elif not self.required and not value: 271 return [] 262 272 if not isinstance(value, (list, tuple)): 263 273 raise ValidationError(u'Enter a list of values.') 264 if self.required and not value:265 raise ValidationError(u'This field is required.')266 274 new_value = [] 267 275 for val in value: … … 278 286 def __init__(self, fields=(), required=True, widget=None): 279 287 Field.__init__(self, required, widget) 288 # Set 'required' to False on the individual fields, because the 289 # required validation will be handled by ComboField, not by those 290 # individual fields. 291 for f in fields: 292 f.required = False 280 293 self.fields = fields 281 294 django/branches/multiple-db-support/django/newforms/forms.py
r4158 r4159 4 4 5 5 from django.utils.datastructures import SortedDict 6 from django.utils.html import escape 6 7 from fields import Field 7 from widgets import TextInput, Textarea 8 from widgets import TextInput, Textarea, HiddenInput 8 9 from util import ErrorDict, ErrorList, ValidationError 9 10 … … 37 38 38 39 def __init__(self, data=None, auto_id=False): # TODO: prefix stuff 40 self.ignore_errors = data is None 39 41 self.data = data or {} 40 42 self.auto_id = auto_id … … 57 59 return BoundField(self, field, name) 58 60 59 def clean(self): 60 if self.__errors is None: 61 self.full_clean() 62 return self.clean_data 63 64 def errors(self): 61 def _errors(self): 65 62 "Returns an ErrorDict for self.data" 66 63 if self.__errors is None: 67 64 self.full_clean() 68 65 return self.__errors 66 errors = property(_errors) 69 67 70 68 def is_valid(self): 71 69 """ 72 Returns True if the form has no errors. Otherwise, False. This exists 73 solely for convenience, so client code can use positive logic rather 74 than confusing negative logic ("if not form.errors()"). 75 """ 76 return not bool(self.errors()) 70 Returns True if the form has no errors. Otherwise, False. If errors are 71 being ignored, returns False. 72 """ 73 return not self.ignore_errors and not bool(self.errors) 77 74 78 75 def as_table(self): 79 76 "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." 80 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()]) 77 output = [] 78 if self.errors.get(NON_FIELD_ERRORS): 79 # Errors not corresponding to a particular field are displayed at the top. 80 output.append(u'<tr><td colspan="2">%s</td></tr>' % self.non_field_errors()) 81 for name, field in self.fields.items(): 82 bf = BoundField(self, field, name) 83 if bf.is_hidden: 84 if bf.errors: 85 new_errors = ErrorList(['(Hidden field %s) %s' % (name, e) for e in bf.errors]) 86 output.append(u'<tr><td colspan="2">%s</td></tr>' % new_errors) 87 output.append(str(bf)) 88 else: 89 if bf.errors: 90 output.append(u'<tr><td colspan="2">%s</td></tr>' % bf.errors) 91 output.append(u'<tr><td>%s</td><td>%s</td></tr>' % (bf.label_tag(escape(bf.verbose_name+':')), bf)) 92 return u'\n'.join(output) 81 93 82 94 def as_ul(self): 83 95 "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." 84 return u'\n'.join(['<li>%s: %s</li>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])85 86 def as_table_with_errors(self):87 "Returns this form rendered as HTML <tr>s, with errors."88 96 output = [] 89 if self.errors ().get(NON_FIELD_ERRORS):97 if self.errors.get(NON_FIELD_ERRORS): 90 98 # Errors not corresponding to a particular field are displayed at the top. 91 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]]))99 output.append(u'<li>%s</li>' % self.non_field_errors()) 92 100 for name, field in self.fields.items(): 93 101 bf = BoundField(self, field, name) 94 if bf.errors: 95 output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors])) 96 output.append('<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf)) 102 if bf.is_hidden: 103 if bf.errors: 104 new_errors = ErrorList(['(Hidden field %s) %s' % (name, e) for e in bf.errors]) 105 output.append(u'<li>%s</li>' % new_errors) 106 output.append(str(bf)) 107 else: 108 output.append(u'<li>%s%s %s</li>' % (bf.errors, bf.label_tag(escape(bf.verbose_name+':')), bf)) 97 109 return u'\n'.join(output) 98 110 99 def as_ul_with_errors(self): 100 "Returns this form rendered as HTML <li>s, with errors." 101 output = [] 102 if self.errors().get(NON_FIELD_ERRORS): 103 # Errors not corresponding to a particular field are displayed at the top. 104 output.append('<li><ul>%s</ul></li>' % '\n'.join(['<li>%s</li>' % e for e in self.errors()[NON_FIELD_ERRORS]])) 105 for name, field in self.fields.items(): 106 bf = BoundField(self, field, name) 107 line = '<li>' 108 if bf.errors: 109 line += '<ul>%s</ul>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors]) 110 line += '%s: %s</li>' % (pretty_name(name), bf) 111 output.append(line) 112 return u'\n'.join(output) 111 def non_field_errors(self): 112 """ 113 Returns an ErrorList of errors that aren't associated with a particular 114 field -- i.e., from Form.clean(). Returns an empty ErrorList if there 115 are none. 116 """ 117 return self.errors.get(NON_FIELD_ERRORS, ErrorList()) 113 118 114 119 def full_clean(self): … … 118 123 self.clean_data = {} 119 124 errors = ErrorDict() 120 for name, field in self.fields.items(): 121 value = self.data.get(name, None) 125 if self.ignore_errors: # Stop further processing. 126 self.__errors = errors 127 return 128 for name, field in self.fields.items(): 129 # value_from_datadict() gets the data from the dictionary. 130 # Each widget type knows how to retrieve its own data, because some 131 # widgets split data over several HTML fields. 132 value = field.widget.value_from_datadict(self.data, name) 122 133 try: 123 134 value = field.clean(value) … … 139 150 """ 140 151 Hook for doing any extra form-wide cleaning after Field.clean() been 141 called on every field. 152 called on every field. Any ValidationError raised by this method will 153 not be associated with a particular field; it will have a special-case 154 association with the field named '__all__'. 142 155 """ 143 156 return self.clean_data … … 154 167 # Use the 'widget' attribute on the field to determine which type 155 168 # of HTML widget to use. 156 return self.as_widget(self._field.widget) 169 value = self.as_widget(self._field.widget) 170 if not isinstance(value, basestring): 171 # Some Widget render() methods -- notably RadioSelect -- return a 172 # "special" object rather than a string. Call the __str__() on that 173 # object to get its rendered value. 174 value = value.__str__() 175 return value 157 176 158 177 def _errors(self): … … 162 181 """ 163 182 try: 164 return self._form.errors ()[self._name]183 return self._form.errors[self._name] 165 184 except KeyError: 166 185 return ErrorList() … … 170 189 attrs = attrs or {} 171 190 auto_id = self.auto_id 172 if not attrs.has_key('id') and not widget.attrs.has_key('id') and auto_id:191 if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'): 173 192 attrs['id'] = auto_id 174 return widget.render(self._name, self. _form.data.get(self._name, None), attrs=attrs)193 return widget.render(self._name, self.data, attrs=attrs) 175 194 176 195 def as_text(self, attrs=None): … … 183 202 "Returns a string of HTML for representing this as a <textarea>." 184 203 return self.as_widget(Textarea(), attrs) 204 205 def as_hidden(self, attrs=None): 206 """ 207 Returns a string of HTML for representing this as an <input type="hidden">. 208 """ 209 return self.as_widget(HiddenInput(), attrs) 210 211 def _data(self): 212 "Returns the data for this BoundField, or None if it wasn't given." 213 return self._form.data.get(self._name, None) 214 data = property(_data) 215 216 def _verbose_name(self): 217 return pretty_name(self._name) 218 verbose_name = property(_verbose_name) 219 220 def label_tag(self, contents=None): 221 """ 222 Wraps the given contents in a <label>, if the field has an ID attribute. 223 Does not HTML-escape the contents. If contents aren't given, uses the 224 field's HTML-escaped verbose_name. 225 """ 226 contents = contents or escape(self.verbose_name) 227 widget = self._field.widget 228 id_ = widget.attrs.get('id') or self.auto_id 229 if id_: 230 contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents) 231 return contents 232 233 def _is_hidden(self): 234 "Returns True if this BoundField's widget is hidden." 235 return self._field.widget.is_hidden 236 is_hidden = property(_is_hidden) 185 237 186 238 def _auto_id(self): django/branches/multiple-db-support/django/newforms/__init__.py
r4156 r4159 15 15 from fields import * 16 16 from forms import Form 17 18 ########################## 19 # DATABASE API SHORTCUTS # 20 ########################## 21 22 def form_for_model(model): 23 "Returns a Form instance for the given Django model class." 24 raise NotImplementedError 25 26 def form_for_fields(field_list): 27 "Returns a Form instance for the given list of Django database field instances." 28 raise NotImplementedError 17 from models import * django/branches/multiple-db-support/django/newforms/widgets.py
r4158 r4159 6 6 'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput', 7 7 'Textarea', 'CheckboxInput', 8 'Select', 'SelectMultiple', 'RadioSelect', 8 'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple', 9 9 ) 10 10 … … 24 24 class Widget(object): 25 25 requires_data_list = False # Determines whether render()'s 'value' argument should be a list. 26 is_hidden = False # Determines whether this corresponds to an <input type="hidden">. 27 26 28 def __init__(self, attrs=None): 27 29 self.attrs = attrs or {} … … 31 33 32 34 def build_attrs(self, extra_attrs=None, **kwargs): 35 "Helper function for building an attribute dictionary." 33 36 attrs = dict(self.attrs, **kwargs) 34 37 if extra_attrs: 35 38 attrs.update(extra_attrs) 36 39 return attrs 40 41 def value_from_datadict(self, data, name): 42 """ 43 Given a dictionary of data and this widget's name, returns the value 44 of this widget. Returns None if it's not provided. 45 """ 46 return data.get(name, None) 47 48 def id_for_label(self, id_): 49 """ 50 Returns the HTML ID attribute of this Widget for use by a <label>, 51 given the ID of the field. Returns None if no ID is available. 52 53 This hook is necessary because some widgets have multiple HTML 54 elements and, thus, multiple IDs. In that case, this method should 55 return an ID value that corresponds to the first ID in the widget's 56 tags. 57 """ 58 return id_ 59 id_for_label = classmethod(id_for_label) 37 60 38 61 class Input(Widget): … … 56 79 class HiddenInput(Input): 57 80 input_type = 'hidden' 81 is_hidden = True 58 82 59 83 class FileInput(Input): … … 68 92 69 93 class CheckboxInput(Widget): 94 def __init__(self, attrs=None, check_test=bool): 95 # check_test is a callable that takes a value and returns True 96 # if the checkbox should be checked for that value. 97 self.attrs = attrs or {} 98 self.check_test = check_test 99 70 100 def render(self, name, value, attrs=None): 71 101 final_attrs = self.build_attrs(attrs, type='checkbox', name=name) 72 if value: final_attrs['checked'] = 'checked' 102 try: 103 result = self.check_test(value) 104 except: # Silently catch exceptions 105 result = False 106 if result: 107 final_attrs['checked'] = 'checked' 108 if value not in ('', True, False, None): 109 final_attrs['value'] = smart_unicode(value) # Only add the 'value' attribute if a value is non-empty. 73 110 return u'<input%s />' % flatatt(final_attrs) 74 111 … … 112 149 class RadioInput(object): 113 150 "An object used by RadioFieldRenderer that represents a single <input type='radio'>." 114 def __init__(self, name, value, attrs, choice ):151 def __init__(self, name, value, attrs, choice, index): 115 152 self.name, self.value = name, value 116 self.attrs = attrs or {}153 self.attrs = attrs 117 154 self.choice_value, self.choice_label = choice 155 self.index = index 118 156 119 157 def __str__(self): … … 124 162 125 163 def tag(self): 164 if self.attrs.has_key('id'): 165 self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index) 126 166 final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value) 127 167 if self.is_checked(): … … 136 176 137 177 def __iter__(self): 138 for choice in self.choices:139 yield RadioInput(self.name, self.value, self.attrs , choice)178 for i, choice in enumerate(self.choices): 179 yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i) 140 180 141 181 def __str__(self): … … 148 188 if value is None: value = '' 149 189 str_value = smart_unicode(value) # Normalize to string. 190 attrs = attrs or {} 150 191 return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices))) 151 192 152 class CheckboxSelectMultiple(Widget): 153 pass 193 def id_for_label(self, id_): 194 # RadioSelect is represented by multiple <input type="radio"> fields, 195 # each of which has a distinct ID. The IDs are made distinct by a "_X" 196 # suffix, where X is the zero-based index of the radio field. Thus, 197 # the label for a RadioSelect should reference the first one ('_0'). 198 if id_: 199 id_ += '_0' 200 return id_ 201 id_for_label = classmethod(id_for_label) 202 203 class CheckboxSelectMultiple(SelectMultiple): 204 def render(self, name, value, attrs=None, choices=()): 205 if value is None: value = [] 206 final_attrs = self.build_attrs(attrs, name=name) 207 output = [u'<ul>'] 208 str_values = set([smart_unicode(v) for v in value]) # Normalize to strings. 209 cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values) 210 for option_value, option_label in chain(self.choices, choices): 211 option_value = smart_unicode(option_value) 212 rendered_cb = cb.render(name, option_value) 213 output.append(u'<li><label>%s %s</label></li>' % (rendered_cb, escape(smart_unicode(option_label)))) 214 output.append(u'</ul>') 215 return u'\n'.join(output) 216 217 def id_for_label(self, id_): 218 # See the comment for RadioSelect.id_for_label() 219 if id_: 220 id_ += '_0' 221 return id_ 222 id_for_label = classmethod(id_for_label) django/branches/multiple-db-support/django/views/generic/list_detail.py
r3071 r4159 85 85 mimetype=None): 86 86 """ 87 Generic list of objects.87 Generic detail of an object. 88 88 89 89 Templates: ``<app_label>/<model_name>_detail.html`` django/branches/multiple-db-support/docs/sessions.txt
r3581 r4159 142 142 143 143 def login(request): 144 if request. POST:144 if request.method == 'POST': 145 145 if request.session.test_cookie_worked(): 146 146 request.session.delete_test_cookie() django/branches/multiple-db-support/docs/testing.txt
r4158 r4159 134 134 135 135 For developers new to testing, however, this choice can seem 136 confusing, so here are a few key differences to help you decide w eather136 confusing, so here are a few key differences to help you decide whether 137 137 doctests or unit tests are right for you. 138 138 django/branches/multiple-db-support/setup.py
r4155 r4159 12 12 # an easy way to do this. 13 13 packages, data_files = [], [] 14 root_dir = os.path.join(os.path.dirname(__file__), 'django') 15 for dirpath, dirnames, filenames in os.walk(root_dir): 14 root_dir = os.path.dirname(__file__) 15 len_root_dir = len(root_dir) 16 django_dir = os.path.join(root_dir, 'django') 17 18 for dirpath, dirnames, filenames in os.walk(django_dir): 16 19 # Ignore dirnames that start with '.' 17 20 for i, dirname in enumerate(dirnames): 18 21 if dirname.startswith('.'): del dirnames[i] 19 22 if '__init__.py' in filenames: 20 packages.append(dirpath.replace('/', '.')) 23 package = dirpath[len_root_dir:].lstrip('/').replace('/', '.') 24 packages.append(package) 21 25 else: 22 26 data_files.append((dirpath, [os.path.join(dirpath, f) for f in filenames])) django/branches/multiple-db-support/tests/regressiontests/forms/tests.py
r4158 r4159 4 4 >>> import datetime 5 5 >>> import re 6 7 ########### 8 # Widgets # 9 ########### 10 11 Each Widget class corresponds to an HTML form widget. A Widget knows how to 12 render itself, given a field name and some data. Widgets don't perform 13 validation. 6 14 7 15 # TextInput Widget ############################################################ … … 157 165 >>> w.render('is_cool', '') 158 166 u'<input type="checkbox" name="is_cool" />' 167 >>> w.render('is_cool', None) 168 u'<input type="checkbox" name="is_cool" />' 159 169 >>> w.render('is_cool', False) 160 170 u'<input type="checkbox" name="is_cool" />' 161 171 >>> w.render('is_cool', True) 162 172 u'<input checked="checked" type="checkbox" name="is_cool" />' 173 174 Using any value that's not in ('', None, False, True) will check the checkbox 175 and set the 'value' attribute. 176 >>> w.render('is_cool', 'foo') 177 u'<input checked="checked" type="checkbox" name="is_cool" value="foo" />' 178 163 179 >>> w.render('is_cool', False, attrs={'class': 'pretty'}) 164 180 u'<input type="checkbox" name="is_cool" class="pretty" />' … … 173 189 >>> w.render('is_cool', '', attrs={'class': 'special'}) 174 190 u'<input type="checkbox" class="special" name="is_cool" />' 191 192 You can pass 'check_test' to the constructor. This is a callable that takes the 193 value and returns True if the box should be checked. 194 >>> w = CheckboxInput(check_test=lambda value: value.startswith('hello')) 195 >>> w.render('greeting', '') 196 u'<input type="checkbox" name="greeting" />' 197 >>> w.render('greeting', 'hello') 198 u'<input checked="checked" type="checkbox" name="greeting" value="hello" />' 199 >>> w.render('greeting', 'hello there') 200 u'<input checked="checked" type="checkbox" name="greeting" value="hello there" />' 201 >>> w.render('greeting', 'hello & goodbye') 202 u'<input checked="checked" type="checkbox" name="greeting" value="hello & goodbye" />' 203 204 A subtlety: If the 'check_test' argument cannot handle a value and raises any 205 exception during its __call__, then the exception will be swallowed and the box 206 will not be checked. In this example, the 'check_test' assumes the value has a 207 startswith() method, which fails for the values True, False and None. 208 >>> w.render('greeting', True) 209 u'<input type="checkbox" name="greeting" />' 210 >>> w.render('greeting', False) 211 u'<input type="checkbox" name="greeting" />' 212 >>> w.render('greeting', None) 213 u'<input type="checkbox" name="greeting" />' 175 214 176 215 # Select Widget ############################################################### … … 476 515 beatle J R Ringo False 477 516 517 # CheckboxSelectMultiple Widget ############################################### 518 519 >>> w = CheckboxSelectMultiple() 520 >>> print w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) 521 <ul> 522 <li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> 523 <li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> 524 <li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> 525 <li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> 526 </ul> 527 >>> print w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) 528 <ul> 529 <li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> 530 <li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li> 531 <li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> 532 <li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> 533 </ul> 534 >>> print w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) 535 <ul> 536 <li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> 537 <li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li> 538 <li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> 539 <li><label><input checked="checked" type="checkbox" name="beatles" value="R" /> Ringo</label></li> 540 </ul> 541 542 If the value is None, none of the options are selected: 543 >>> print w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) 544 <ul> 545 <li><label><input type="checkbox" name="beatles" value="J" /> John</label></li> 546 <li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> 547 <li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> 548 <li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> 549 </ul> 550 551 If the value corresponds to a label (but not to an option value), none of the options are selected: 552 >>> print w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) 553 <ul> 554 <li><label><input type="checkbox" name="beatles" value="J" /> John</label></li> 555 <li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> 556 <li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> 557 <li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> 558 </ul> 559 560 If multiple values are given, but some of them are not valid, the valid ones are selected: 561 >>> print w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) 562 <ul> 563 <li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> 564 <li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> 565 <li><label><input checked="checked" type="checkbox" name="beatles" value="G" /> George</label></li> 566 <li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> 567 </ul> 568 569 The value is compared to its str(): 570 >>> print w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]) 571 <ul> 572 <li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> 573 <li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> 574 <li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> 575 </ul> 576 >>> print w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]) 577 <ul> 578 <li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> 579 <li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> 580 <li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> 581 </ul> 582 >>> print w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]) 583 <ul> 584 <li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> 585 <li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> 586 <li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> 587 </ul> 588 589 The 'choices' argument can be any iterable: 590 >>> def get_choices(): 591 ... for i in range(5): 592 ... yield (i, i) 593 >>> print w.render('nums', [2], choices=get_choices()) 594 <ul> 595 <li><label><input type="checkbox" name="nums" value="0" /> 0</label></li> 596 <li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> 597 <li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> 598 <li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> 599 <li><label><input type="checkbox" name="nums" value="4" /> 4</label></li> 600 </ul> 601 602 You can also pass 'choices' to the constructor: 603 >>> w = CheckboxSelectMultiple(choices=[(1, 1), (2, 2), (3, 3)]) 604 >>> print w.render('nums', [2]) 605 <ul> 606 <li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> 607 <li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> 608 <li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> 609 </ul> 610 611 If 'choices' is passed to both the constructor and render(), then they'll both be in the output: 612 >>> print w.render('nums', [2], choices=[(4, 4), (5, 5)]) 613 <ul> 614 <li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> 615 <li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> 616 <li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> 617 <li><label><input type="checkbox" name="nums" value="4" /> 4</label></li> 618 <li><label><input type="checkbox" name="nums" value="5" /> 5</label></li> 619 </ul> 620 621 >>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) 622 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>' 623 624 ########## 625 # Fields # 626 ########## 627 628 Each Field class does some sort of validation. Each Field has a clean() method, 629 which either raises django.newforms.ValidationError or returns the "clean" 630 data -- usually a Unicode object, but, in some rare cases, a list. 631 632 Each Field's __init__() takes at least these parameters: 633 required -- Boolean that specifies whether the field is required. 634 True by default. 635 widget -- A Widget class, or instance of a Widget class, that should be 636 used for this Field when displaying it. Each Field has a default 637 Widget that it'll use if you don't specify this. In most cases, 638 the default widget is TextInput. 639 640 Other than that, the Field subclasses have class-specific options for 641 __init__(). For example, CharField has a max_length option. 642 478 643 # CharField ################################################################### 644 645 >>> f = CharField() 646 >>> f.clean(1) 647 u'1' 648 >>> f.clean('hello') 649 u'hello' 650 >>> f.clean(None) 651 Traceback (most recent call last): 652 ... 653 ValidationError: [u'This field is required.'] 654 >>> f.clean('') 655 Traceback (most recent call last): 656 ... 657 ValidationError: [u'This field is required.'] 658 >>> f.clean([1, 2, 3]) 659 u'[1, 2, 3]' 479 660 480 661 >>> f = CharField(required=False) … … 485 666 >>> f.clean(None) 486 667 u'' 668 >>> f.clean('') 669 u'' 487 670 >>> f.clean([1, 2, 3]) 488 671 u'[1, 2, 3]' … … 490 673 CharField accepts an optional max_length parameter: 491 674 >>> f = CharField(max_length=10, required=False) 492 >>> f.clean('')493 u''494 675 >>> f.clean('12345') 495 676 u'12345' … … 519 700 520 701 >>> f = IntegerField() 702 >>> f.clean('') 703 Traceback (most recent call last): 704 ... 705 ValidationError: [u'This field is required.'] 706 >>> f.clean(None) 707
