Changeset 4552
- Timestamp:
- 02/20/07 23:14:28 (2 years ago)
- Files:
-
- django/trunk/django/newforms/models.py (modified) (2 diffs)
- django/trunk/tests/modeltests/model_forms/models.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/newforms/models.py
r4548 r4552 4 4 """ 5 5 6 from django.utils.translation import gettext 7 from util import ValidationError 6 8 from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList 7 from fields import ChoiceField, MultipleChoiceField8 from widgets import Select, SelectMultiple 9 from fields import Field, ChoiceField 10 from widgets import Select, SelectMultiple, MultipleHiddenInput 9 11 10 12 __all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields', … … 105 107 return type('FormForFields', (BaseForm,), {'base_fields': fields}) 106 108 109 class QuerySetIterator(object): 110 def __init__(self, queryset, empty_label, cache_choices): 111 self.queryset, self.empty_label, self.cache_choices = queryset, empty_label, cache_choices 112 113 def __iter__(self): 114 if self.empty_label is not None: 115 yield (u"", self.empty_label) 116 for obj in self.queryset: 117 yield (obj._get_pk_val(), str(obj)) 118 # Clear the QuerySet cache if required. 119 if not self.cache_choices: 120 self.queryset._result_cache = None 121 107 122 class ModelChoiceField(ChoiceField): 108 123 "A ChoiceField whose choices are a model QuerySet." 109 def __init__(self, queryset, empty_label=u"---------", **kwargs): 110 self.model = queryset.model 111 choices = [(obj._get_pk_val(), str(obj)) for obj in queryset] 112 if empty_label is not None: 113 choices = [(u"", empty_label)] + choices 114 ChoiceField.__init__(self, choices=choices, **kwargs) 124 # This class is a subclass of ChoiceField for purity, but it doesn't 125 # actually use any of ChoiceField's implementation. 126 def __init__(self, queryset, empty_label=u"---------", cache_choices=False, 127 required=True, widget=Select, label=None, initial=None, help_text=None): 128 self.queryset = queryset 129 self.empty_label = empty_label 130 self.cache_choices = cache_choices 131 # Call Field instead of ChoiceField __init__() because we don't need 132 # ChoiceField.__init__(). 133 Field.__init__(self, required, widget, label, initial, help_text) 134 self.widget.choices = self.choices 135 136 def _get_choices(self): 137 # If self._choices is set, then somebody must have manually set 138 # the property self.choices. In this case, just return self._choices. 139 if hasattr(self, '_choices'): 140 return self._choices 141 # Otherwise, execute the QuerySet in self.queryset to determine the 142 # choices dynamically. 143 return QuerySetIterator(self.queryset, self.empty_label, self.cache_choices) 144 145 def _set_choices(self, value): 146 # This method is copied from ChoiceField._set_choices(). It's necessary 147 # because property() doesn't allow a subclass to overwrite only 148 # _get_choices without implementing _set_choices. 149 self._choices = self.widget.choices = list(value) 150 151 choices = property(_get_choices, _set_choices) 115 152 116 153 def clean(self, value): 117 value = ChoiceField.clean(self, value)118 if not value:154 Field.clean(self, value) 155 if value in ('', None): 119 156 return None 120 157 try: 121 value = self. model._default_manager.get(pk=value)122 except self. model.DoesNotExist:158 value = self.queryset.model._default_manager.get(pk=value) 159 except self.queryset.model.DoesNotExist: 123 160 raise ValidationError(gettext(u'Select a valid choice. That choice is not one of the available choices.')) 124 161 return value 125 162 126 class ModelMultipleChoiceField(M ultipleChoiceField):163 class ModelMultipleChoiceField(ModelChoiceField): 127 164 "A MultipleChoiceField whose choices are a model QuerySet." 128 def __init__(self, queryset, **kwargs): 129 self.model = queryset.model 130 MultipleChoiceField.__init__(self, choices=[(obj._get_pk_val(), str(obj)) for obj in queryset], **kwargs) 165 hidden_widget = MultipleHiddenInput 166 def __init__(self, queryset, cache_choices=False, required=True, 167 widget=SelectMultiple, label=None, initial=None, help_text=None): 168 super(ModelMultipleChoiceField, self).__init__(queryset, None, cache_choices, 169 required, widget, label, initial, help_text) 131 170 132 171 def clean(self, value): 133 value = MultipleChoiceField.clean(self, value) 134 if not value: 172 if self.required and not value: 173 raise ValidationError(gettext(u'This field is required.')) 174 elif not self.required and not value: 135 175 return [] 136 return self.model._default_manager.filter(pk__in=value) 176 if not isinstance(value, (list, tuple)): 177 raise ValidationError(gettext(u'Enter a list of values.')) 178 final_values = [] 179 for val in value: 180 try: 181 obj = self.queryset.model._default_manager.get(pk=val) 182 except self.queryset.model.DoesNotExist: 183 raise ValidationError(gettext(u'Select a valid choice. %s is not one of the available choices.') % val) 184 else: 185 final_values.append(obj) 186 return final_values django/trunk/tests/modeltests/model_forms/models.py
r4548 r4552 290 290 <Category: Third> 291 291 292 Here, we demonstrate that choices for a ForeignKey ChoiceField are determined 293 at runtime, based on the data in the database when the form is displayed, not 294 the data in the database when the form is instantiated. 295 >>> ArticleForm = form_for_model(Article) 296 >>> f = ArticleForm(auto_id=False) 297 >>> print f.as_ul() 298 <li>Headline: <input type="text" name="headline" maxlength="50" /></li> 299 <li>Pub date: <input type="text" name="pub_date" /></li> 300 <li>Writer: <select name="writer"> 301 <option value="" selected="selected">---------</option> 302 <option value="1">Mike Royko</option> 303 <option value="2">Bob Woodward</option> 304 </select></li> 305 <li>Article: <textarea name="article"></textarea></li> 306 <li>Categories: <select multiple="multiple" name="categories"> 307 <option value="1">Entertainment</option> 308 <option value="2">It's a test</option> 309 <option value="3">Third</option> 310 </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> 311 >>> Category.objects.create(name='Fourth', url='4th') 312 <Category: Fourth> 313 >>> Writer.objects.create(name='Carl Bernstein') 314 <Writer: Carl Bernstein> 315 >>> print f.as_ul() 316 <li>Headline: <input type="text" name="headline" maxlength="50" /></li> 317 <li>Pub date: <input type="text" name="pub_date" /></li> 318 <li>Writer: <select name="writer"> 319 <option value="" selected="selected">---------</option> 320 <option value="1">Mike Royko</option> 321 <option value="2">Bob Woodward</option> 322 <option value="3">Carl Bernstein</option> 323 </select></li> 324 <li>Article: <textarea name="article"></textarea></li> 325 <li>Categories: <select multiple="multiple" name="categories"> 326 <option value="1">Entertainment</option> 327 <option value="2">It's a test</option> 328 <option value="3">Third</option> 329 <option value="4">Fourth</option> 330 </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> 331 292 332 # ModelChoiceField ############################################################ 293 333 … … 311 351 >>> f.clean(2) 312 352 <Category: It's a test> 353 354 # Add a Category object *after* the ModelChoiceField has already been 355 # instantiated. This proves clean() checks the database during clean() rather 356 # than caching it at time of instantiation. 357 >>> Category.objects.create(name='Fifth', url='5th') 358 <Category: Fifth> 359 >>> f.clean(5) 360 <Category: Fifth> 361 362 # Delete a Category object *after* the ModelChoiceField has already been 363 # instantiated. This proves clean() checks the database during clean() rather 364 # than caching it at time of instantiation. 365 >>> Category.objects.get(url='5th').delete() 366 >>> f.clean(5) 367 Traceback (most recent call last): 368 ... 369 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] 313 370 314 371 >>> f = ModelChoiceField(Category.objects.filter(pk=1), required=False) … … 318 375 >>> f.clean('1') 319 376 <Category: Entertainment> 320 >>> f.clean(' 2')377 >>> f.clean('100') 321 378 Traceback (most recent call last): 322 379 ... … … 346 403 >>> f.clean((1, '2')) 347 404 [<Category: Entertainment>, <Category: It's a test>] 348 >>> f.clean([' nonexistent'])349 Traceback (most recent call last): 350 ... 351 ValidationError: [u'Select a valid choice. nonexistentis not one of the available choices.']405 >>> f.clean(['100']) 406 Traceback (most recent call last): 407 ... 408 ValidationError: [u'Select a valid choice. 100 is not one of the available choices.'] 352 409 >>> f.clean('hello') 353 410 Traceback (most recent call last): 354 411 ... 355 412 ValidationError: [u'Enter a list of values.'] 413 414 # Add a Category object *after* the ModelChoiceField has already been 415 # instantiated. This proves clean() checks the database during clean() rather 416 # than caching it at time of instantiation. 417 >>> Category.objects.create(id=6, name='Sixth', url='6th') 418 <Category: Sixth> 419 >>> f.clean([6]) 420 [<Category: Sixth>] 421 422 # Delete a Category object *after* the ModelChoiceField has already been 423 # instantiated. This proves clean() checks the database during clean() rather 424 # than caching it at time of instantiation. 425 >>> Category.objects.get(url='6th').delete() 426 >>> f.clean([6]) 427 Traceback (most recent call last): 428 ... 429 ValidationError: [u'Select a valid choice. 6 is not one of the available choices.'] 430 356 431 >>> f = ModelMultipleChoiceField(Category.objects.all(), required=False) 357 432 >>> f.clean([]) … … 359 434 >>> f.clean(()) 360 435 [] 361 >>> f.clean([' 4'])362 Traceback (most recent call last): 363 ... 364 ValidationError: [u'Select a valid choice. 4is not one of the available choices.']365 >>> f.clean(['3', ' 4'])366 Traceback (most recent call last): 367 ... 368 ValidationError: [u'Select a valid choice. 4is not one of the available choices.']369 >>> f.clean(['1', ' 5'])370 Traceback (most recent call last): 371 ... 372 ValidationError: [u'Select a valid choice. 5is not one of the available choices.']436 >>> f.clean(['10']) 437 Traceback (most recent call last): 438 ... 439 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.'] 440 >>> f.clean(['3', '10']) 441 Traceback (most recent call last): 442 ... 443 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.'] 444 >>> f.clean(['1', '10']) 445 Traceback (most recent call last): 446 ... 447 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.'] 373 448 """}
