Changeset 7977
- Timestamp:
- 07/19/08 02:53:02 (3 months ago)
- Files:
-
- django/trunk/django/db/models/base.py (modified) (1 diff)
- django/trunk/django/db/models/fields/__init__.py (modified) (2 diffs)
- django/trunk/django/forms/fields.py (modified) (3 diffs)
- django/trunk/django/forms/widgets.py (modified) (2 diffs)
- django/trunk/docs/model-api.txt (modified) (1 diff)
- django/trunk/docs/newforms.txt (modified) (2 diffs)
- django/trunk/tests/regressiontests/forms/fields.py (modified) (8 diffs)
- django/trunk/tests/regressiontests/forms/widgets.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/db/models/base.py
r7967 r7977 427 427 def _get_FIELD_display(self, field): 428 428 value = getattr(self, field.attname) 429 return force_unicode(dict(field. choices).get(value, value), strings_only=True)429 return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True) 430 430 431 431 def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs): django/trunk/django/db/models/fields/__init__.py
r7971 r7977 289 289 field_objs = [oldforms.SelectField] 290 290 291 params['choices'] = self. get_choices_default()291 params['choices'] = self.flatchoices 292 292 else: 293 293 field_objs = self.get_manipulator_field_objs() … … 408 408 choices = property(_get_choices) 409 409 410 def _get_flatchoices(self): 411 flat = [] 412 for choice, value in self.get_choices_default(): 413 if type(value) in (list, tuple): 414 flat.extend(value) 415 else: 416 flat.append((choice,value)) 417 return flat 418 flatchoices = property(_get_flatchoices) 419 410 420 def save_form_data(self, instance, data): 411 421 setattr(instance, self.name, data) django/trunk/django/forms/fields.py
r7971 r7977 586 586 widget = Select 587 587 default_error_messages = { 588 'invalid_choice': _(u'Select a valid choice. That choiceis not one of the available choices.'),588 'invalid_choice': _(u'Select a valid choice. %(value)s is not one of the available choices.'), 589 589 } 590 590 … … 616 616 if value == u'': 617 617 return value 618 valid_values = set([smart_unicode(k) for k, v in self.choices]) 619 if value not in valid_values: 618 if not self.valid_value(value): 620 619 raise ValidationError(self.error_messages['invalid_choice'] % {'value': value}) 621 620 return value 622 621 622 def valid_value(self, value): 623 "Check to see if the provided value is a valid choice" 624 for k, v in self.choices: 625 if type(v) in (tuple, list): 626 # This is an optgroup, so look inside the group for options 627 for k2, v2 in v: 628 if value == smart_unicode(k2): 629 return True 630 else: 631 if value == smart_unicode(k): 632 return True 633 return False 634 623 635 class MultipleChoiceField(ChoiceField): 624 636 hidden_widget = MultipleHiddenInput … … 641 653 new_value = [smart_unicode(val) for val in value] 642 654 # Validate that each value in the value list is in self.choices. 643 valid_values = set([smart_unicode(k) for k, v in self.choices])644 655 for val in new_value: 645 if val not in valid_values:656 if not self.valid_value(val): 646 657 raise ValidationError(self.error_messages['invalid_choice'] % {'value': val}) 647 658 return new_value django/trunk/django/forms/widgets.py
r7971 r7977 346 346 final_attrs = self.build_attrs(attrs, name=name) 347 347 output = [u'<select%s>' % flatatt(final_attrs)] 348 # Normalize to string. 349 str_value = force_unicode(value) 348 options = self.render_options(choices, [value]) 349 if options: 350 output.append(options) 351 output.append('</select>') 352 return mark_safe(u'\n'.join(output)) 353 354 def render_options(self, choices, selected_choices): 355 def render_option(option_value, option_label): 356 option_value = force_unicode(option_value) 357 selected_html = (option_value in selected_choices) and u' selected="selected"' or '' 358 return u'<option value="%s"%s>%s</option>' % ( 359 escape(option_value), selected_html, 360 conditional_escape(force_unicode(option_label))) 361 # Normalize to strings. 362 selected_choices = set([force_unicode(v) for v in selected_choices]) 363 output = [] 350 364 for option_value, option_label in chain(self.choices, choices): 351 option_value = force_unicode(option_value) 352 selected_html = (option_value == str_value) and u' selected="selected"' or '' 353 output.append(u'<option value="%s"%s>%s</option>' % ( 354 escape(option_value), selected_html, 355 conditional_escape(force_unicode(option_label)))) 356 output.append(u'</select>') 357 return mark_safe(u'\n'.join(output)) 365 if isinstance(option_label, (list, tuple)): 366 output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value))) 367 for option in option_label: 368 output.append(render_option(*option)) 369 output.append(u'</optgroup>') 370 else: 371 output.append(render_option(option_value, option_label)) 372 return u'\n'.join(output) 358 373 359 374 class NullBooleanSelect(Select): … … 381 396 return bool(initial) != bool(data) 382 397 383 class SelectMultiple(Widget): 384 def __init__(self, attrs=None, choices=()): 385 super(SelectMultiple, self).__init__(attrs) 386 # choices can be any iterable 387 self.choices = choices 388 398 class SelectMultiple(Select): 389 399 def render(self, name, value, attrs=None, choices=()): 390 400 if value is None: value = [] 391 401 final_attrs = self.build_attrs(attrs, name=name) 392 402 output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)] 393 str_values = set([force_unicode(v) for v in value]) # Normalize to strings. 394 for option_value, option_label in chain(self.choices, choices): 395 option_value = force_unicode(option_value) 396 selected_html = (option_value in str_values) and ' selected="selected"' or '' 397 output.append(u'<option value="%s"%s>%s</option>' % ( 398 escape(option_value), selected_html, 399 conditional_escape(force_unicode(option_label)))) 400 output.append(u'</select>') 403 options = self.render_options(choices, value) 404 if options: 405 output.append(options) 406 output.append('</select>') 401 407 return mark_safe(u'\n'.join(output)) 402 408 django/trunk/docs/model-api.txt
r7967 r7977 554 554 class Foo(models.Model): 555 555 gender = models.CharField(max_length=1, choices=GENDER_CHOICES) 556 557 You can also collect your available choices into named groups that can 558 be used for organizational purposes:: 559 560 MEDIA_CHOICES = ( 561 ('Audio', ( 562 ('vinyl', 'Vinyl'), 563 ('cd', 'CD'), 564 ) 565 ), 566 ('Video', ( 567 ('vhs', 'VHS Tape'), 568 ('dvd', 'DVD'), 569 ) 570 ), 571 ('unknown', 'Unknown'), 572 ) 573 574 The first element in each tuple is the name to apply to the group. The 575 second element is an iterable of 2-tuples, with each 2-tuple containing 576 a value and a human-readable name for an option. Grouped options may be 577 combined with ungrouped options within a single list (such as the 578 `unknown` option in this example). 556 579 557 580 For each model field that has ``choices`` set, Django will add a method to django/trunk/docs/newforms.txt
r7967 r7977 1237 1237 1238 1238 Takes one extra argument, ``choices``, which is an iterable (e.g., a list or 1239 tuple) of 2-tuples to use as choices for this field. 1239 tuple) of 2-tuples to use as choices for this field. This argument accepts 1240 the same formats as the ``choices`` argument to a model field. See the 1241 `model API documentation on choices`_ for more details. 1242 1243 .. _model API documentation on choices: ../model-api#choices 1240 1244 1241 1245 ``DateField`` … … 1445 1449 1446 1450 Takes one extra argument, ``choices``, which is an iterable (e.g., a list or 1447 tuple) of 2-tuples to use as choices for this field. 1451 tuple) of 2-tuples to use as choices for this field. This argument accepts 1452 the same formats as the ``choices`` argument to a model field. See the 1453 `model API documentation on choices`_ for more details. 1448 1454 1449 1455 ``NullBooleanField`` django/trunk/tests/regressiontests/forms/fields.py
r7971 r7977 981 981 # ChoiceField ################################################################# 982 982 983 >>> f = ChoiceField(choices=[('1', ' 1'), ('2', '2')])983 >>> f = ChoiceField(choices=[('1', 'One'), ('2', 'Two')]) 984 984 >>> f.clean('') 985 985 Traceback (most recent call last): … … 997 997 Traceback (most recent call last): 998 998 ... 999 ValidationError: [u'Select a valid choice. That choiceis not one of the available choices.']1000 1001 >>> f = ChoiceField(choices=[('1', ' 1'), ('2', '2')], required=False)999 ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] 1000 1001 >>> f = ChoiceField(choices=[('1', 'One'), ('2', 'Two')], required=False) 1002 1002 >>> f.clean('') 1003 1003 u'' … … 1011 1011 Traceback (most recent call last): 1012 1012 ... 1013 ValidationError: [u'Select a valid choice. That choiceis not one of the available choices.']1013 ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] 1014 1014 1015 1015 >>> f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')]) … … 1019 1019 Traceback (most recent call last): 1020 1020 ... 1021 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] 1021 ValidationError: [u'Select a valid choice. John is not one of the available choices.'] 1022 1023 >>> f = ChoiceField(choices=[('Numbers', (('1', 'One'), ('2', 'Two'))), ('Letters', (('3','A'),('4','B'))), ('5','Other')]) 1024 >>> f.clean(1) 1025 u'1' 1026 >>> f.clean('1') 1027 u'1' 1028 >>> f.clean(3) 1029 u'3' 1030 >>> f.clean('3') 1031 u'3' 1032 >>> f.clean(5) 1033 u'5' 1034 >>> f.clean('5') 1035 u'5' 1036 >>> f.clean('6') 1037 Traceback (most recent call last): 1038 ... 1039 ValidationError: [u'Select a valid choice. 6 is not one of the available choices.'] 1022 1040 1023 1041 # NullBooleanField ############################################################ … … 1037 1055 # MultipleChoiceField ######################################################### 1038 1056 1039 >>> f = MultipleChoiceField(choices=[('1', ' 1'), ('2', '2')])1057 >>> f = MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two')]) 1040 1058 >>> f.clean('') 1041 1059 Traceback (most recent call last): … … 1073 1091 ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] 1074 1092 1075 >>> f = MultipleChoiceField(choices=[('1', ' 1'), ('2', '2')], required=False)1093 >>> f = MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two')], required=False) 1076 1094 >>> f.clean('') 1077 1095 [] … … 1101 1119 ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] 1102 1120 1121 >>> f = MultipleChoiceField(choices=[('Numbers', (('1', 'One'), ('2', 'Two'))), ('Letters', (('3','A'),('4','B'))), ('5','Other')]) 1122 >>> f.clean([1]) 1123 [u'1'] 1124 >>> f.clean(['1']) 1125 [u'1'] 1126 >>> f.clean([1, 5]) 1127 [u'1', u'5'] 1128 >>> f.clean([1, '5']) 1129 [u'1', u'5'] 1130 >>> f.clean(['1', 5]) 1131 [u'1', u'5'] 1132 >>> f.clean(['1', '5']) 1133 [u'1', u'5'] 1134 >>> f.clean(['6']) 1135 Traceback (most recent call last): 1136 ... 1137 ValidationError: [u'Select a valid choice. 6 is not one of the available choices.'] 1138 >>> f.clean(['1','6']) 1139 Traceback (most recent call last): 1140 ... 1141 ValidationError: [u'Select a valid choice. 6 is not one of the available choices.'] 1142 1143 1103 1144 # ComboField ################################################################## 1104 1145 … … 1166 1207 Traceback (most recent call last): 1167 1208 ... 1168 ValidationError: [u'Select a valid choice. That choiceis not one of the available choices.']1209 ValidationError: [u'Select a valid choice. fields.py is not one of the available choices.'] 1169 1210 >>> fix_os_paths(f.clean(path + 'fields.py')) 1170 1211 u'.../django/forms/fields.py' django/trunk/tests/regressiontests/forms/widgets.py
r7971 r7977 459 459 </select> 460 460 461 Choices can be nested one level in order to create HTML optgroups: 462 >>> w.choices=(('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2')))) 463 >>> print w.render('nestchoice', None) 464 <select name="nestchoice"> 465 <option value="outer1">Outer 1</option> 466 <optgroup label="Group "1""> 467 <option value="inner1">Inner 1</option> 468 <option value="inner2">Inner 2</option> 469 </optgroup> 470 </select> 471 472 >>> print w.render('nestchoice', 'outer1') 473 <select name="nestchoice"> 474 <option value="outer1" selected="selected">Outer 1</option> 475 <optgroup label="Group "1""> 476 <option value="inner1">Inner 1</option> 477 <option value="inner2">Inner 2</option> 478 </optgroup> 479 </select> 480 481 >>> print w.render('nestchoice', 'inner1') 482 <select name="nestchoice"> 483 <option value="outer1">Outer 1</option> 484 <optgroup label="Group "1""> 485 <option value="inner1" selected="selected">Inner 1</option> 486 <option value="inner2">Inner 2</option> 487 </optgroup> 488 </select> 489 461 490 # NullBooleanSelect Widget #################################################### 462 491 … … 626 655 >>> w._has_changed([1, 2], [u'1', u'3']) 627 656 True 657 658 # Choices can be nested one level in order to create HTML optgroups: 659 >>> w.choices = (('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2')))) 660 >>> print w.render('nestchoice', None) 661 <select multiple="multiple" name="nestchoice"> 662 <option value="outer1">Outer 1</option> 663 <optgroup label="Group "1""> 664 <option value="inner1">Inner 1</option> 665 <option value="inner2">Inner 2</option> 666 </optgroup> 667 </select> 668 669 >>> print w.render('nestchoice', ['outer1']) 670 <select multiple="multiple" name="nestchoice"> 671 <option value="outer1" selected="selected">Outer 1</option> 672 <optgroup label="Group "1""> 673 <option value="inner1">Inner 1</option> 674 <option value="inner2">Inner 2</option> 675 </optgroup> 676 </select> 677 678 >>> print w.render('nestchoice', ['inner1']) 679 <select multiple="multiple" name="nestchoice"> 680 <option value="outer1">Outer 1</option> 681 <optgroup label="Group "1""> 682 <option value="inner1" selected="selected">Inner 1</option> 683 <option value="inner2">Inner 2</option> 684 </optgroup> 685 </select> 686 687 >>> print w.render('nestchoice', ['outer1', 'inner2']) 688 <select multiple="multiple" name="nestchoice"> 689 <option value="outer1" selected="selected">Outer 1</option> 690 <optgroup label="Group "1""> 691 <option value="inner1">Inner 1</option> 692 <option value="inner2" selected="selected">Inner 2</option> 693 </optgroup> 694 </select> 628 695 629 696 # RadioSelect Widget ##########################################################
