Django

Code

Ticket #4620: 4620_all.diff

File 4620_all.diff, 4.8 kB (added by PhiR, 9 months ago)

patch with fix + doc + tests

  • django/newforms/models.py

    old new  
    278278# Fields ##################################################################### 
    279279 
    280280class QuerySetIterator(object): 
    281     def __init__(self, queryset, empty_label, cache_choices): 
    282         self.queryset = queryset 
    283         self.empty_label = empty_label 
    284         self.cache_choices = cache_choices 
     281    def __init__(self, field): 
     282        self.field = field 
     283        self.queryset = field.queryset 
    285284 
    286285    def __iter__(self): 
    287         if self.empty_label is not None: 
    288             yield (u"", self.empty_label) 
     286        if self.field.empty_label is not None: 
     287            yield (u"", self.field.empty_label) 
    289288        for obj in self.queryset: 
    290             yield (obj.pk, smart_unicode(obj)) 
     289            yield (obj.pk, self.field.label_from_instance(obj)) 
    291290        # Clear the QuerySet cache if required. 
    292         if not self.cache_choices: 
     291        if not self.field.cache_choices: 
    293292            self.queryset._result_cache = None 
    294293 
    295294class ModelChoiceField(ChoiceField): 
     
    321320 
    322321    queryset = property(_get_queryset, _set_queryset) 
    323322 
     323    # this method will be used to create object labels by the QuerySetIterator.  
     324    # Override it to customize the label.  
     325    def label_from_instance(self, obj): 
     326        return smart_unicode(obj) 
     327     
    324328    def _get_choices(self): 
    325329        # If self._choices is set, then somebody must have manually set 
    326330        # the property self.choices. In this case, just return self._choices. 
     
    331335        # been consumed. Note that we're instantiating a new QuerySetIterator 
    332336        # *each* time _get_choices() is called (and, thus, each time 
    333337        # self.choices is accessed) so that we can ensure the QuerySet has not 
    334         # been consumed. 
    335         return QuerySetIterator(self.queryset, self.empty_label, 
    336                                 self.cache_choices
     338        # been consumed. This construct might look complicated but it allows  
     339        # for lazy evaluation of the queryset. 
     340        return QuerySetIterator(self
    337341 
    338342    def _set_choices(self, value): 
    339343        # This method is copied from ChoiceField._set_choices(). It's necessary 
  • tests/modeltests/model_forms/models.py

    old new  
    645645... 
    646646ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] 
    647647 
     648# check that we can safely iterate choices repeatedly 
     649>>> gen_one = list(f.choices) 
     650>>> gen_two = f.choices 
     651>>> gen_one[2] 
     652(2L, u"It's a test") 
     653>>> list(gen_two) 
     654[(u'', u'---------'), (1L, u'Entertainment'), (2L, u"It's a test"), (3L, u'Third')] 
    648655 
     656# check that we can override the label_from_instance method to print custom labels (#4620) 
     657>>> f.queryset = Category.objects.all() 
     658>>> f.label_from_instance = lambda obj: "category " + str(obj) 
     659>>> list(f.choices) 
     660[(u'', u'---------'), (1L, 'category Entertainment'), (2L, "category It's a test"), (3L, 'category Third'), (4L, 'category Fourth')] 
     661 
    649662# ModelMultipleChoiceField #################################################### 
    650663 
    651664>>> f = ModelMultipleChoiceField(Category.objects.all()) 
     
    730743... 
    731744ValidationError: [u'Select a valid choice. 4 is not one of the available choices.'] 
    732745 
     746>>> f.queryset = Category.objects.all() 
     747>>> f.label_from_instance = lambda obj: "multicategory " + str(obj) 
     748>>> list(f.choices) 
     749[(1L, 'multicategory Entertainment'), (2L, "multicategory It's a test"), (3L, 'multicategory Third'), (4L, 'multicategory Fourth')] 
    733750 
    734751# PhoneNumberField ############################################################ 
    735752 
  • docs/newforms.txt

    old new  
    15171517~~~~~~~~~~~~~~~~~~~~ 
    15181518 
    15191519Allows the selection of a single model object, suitable for 
    1520 representing a foreign key. 
     1520representing a foreign key. A ``ModelChoiceField`` will use the  
     1521``__unicode__()`` method of the model to represent them, but this  
     1522can be customized by overriding the ``label_from_instance`` method 
     1523of the field itself (ie subclassing it). The method receives an object  
     1524as an argument and must return a string to represent it.  
    15211525 
    15221526``ModelMultipleChoiceField`` 
    15231527~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    15241528 
    15251529Allows the selection of one or more model objects, suitable for 
    1526 representing a many-to-many relation. 
     1530representing a many-to-many relation. As with ``ModelChoiceField``,  
     1531you can use ``label_from_instance`` to customize the object labels. 
    15271532 
    15281533 
    15291534Creating custom fields