Django

Code

Changeset 7326

Show
Ignore:
Timestamp:
03/19/08 18:10:45 (1 year ago)
Author:
jacob
Message:

Fixed #4620: you can now easily add custom labels to ModelChoiceFields? via subclassing. Thanks, PhiR.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/newforms/models.py

    r7117 r7326  
    278278# Fields ##################################################################### 
    279279 
    280 class 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 
     280class ModelChoiceIterator(object): 
     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 
     
    307306        self.empty_label = empty_label 
    308307        self.cache_choices = cache_choices 
     308         
    309309        # Call Field instead of ChoiceField __init__() because we don't need 
    310310        # ChoiceField.__init__(). 
     
    322322    queryset = property(_get_queryset, _set_queryset) 
    323323 
     324    # this method will be used to create object labels by the QuerySetIterator.  
     325    # Override it to customize the label.  
     326    def label_from_instance(self, obj): 
     327        """ 
     328        This method is used to convert objects into strings; it's used to 
     329        generate the labels for the choices presented by this object. Subclasses 
     330        can override this method to customize the display of the choices. 
     331        """ 
     332        return smart_unicode(obj) 
     333     
    324334    def _get_choices(self): 
    325335        # If self._choices is set, then somebody must have manually set 
     
    327337        if hasattr(self, '_choices'): 
    328338            return self._choices 
     339 
    329340        # Otherwise, execute the QuerySet in self.queryset to determine the 
    330         # choices dynamically. Return a fresh QuerySetIterator that has not 
    331         # been consumed. Note that we're instantiating a new QuerySetIterator 
    332         # *each* time _get_choices() is called (and, thus, each time 
    333         # 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
     341        # choices dynamically. Return a fresh QuerySetIterator that has not been 
     342        # consumed. Note that we're instantiating a new QuerySetIterator *each* 
     343        # time _get_choices() is called (and, thus, each time self.choices is 
     344        # accessed) so that we can ensure the QuerySet has not been consumed. This 
     345        # construct might look complicated but it allows for lazy evaluation of 
     346        # the queryset. 
     347        return ModelChoiceIterator(self
    337348 
    338349    def _set_choices(self, value): 
  • django/trunk/docs/newforms.txt

    r7323 r7326  
    15501550~~~~~~~~~~~~~~~~~~~~ 
    15511551 
    1552 Allows the selection of a single model object, suitable for 
    1553 representing a foreign key. 
     1552Allows the selection of a single model object, suitable for representing a 
     1553foreign key. The method receives an object as an argument and must return a 
     1554string to represent it. 
     1555 
     1556The labels for the choice field call the ``__unicode__`` method of the model to 
     1557generate string representations. To provide custom labels, subclass ``ModelChoiceField`` and override ``label_for_model``:: 
     1558 
     1559    class MyModelChoiceField(ModelChoiceField): 
     1560        def label_from_instance(self, obj): 
     1561            return "My Object #%i" % obj.id 
    15541562 
    15551563``ModelMultipleChoiceField`` 
    15561564~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    15571565 
    1558 Allows the selection of one or more model objects, suitable for 
    1559 representing a many-to-many relation. 
    1560  
     1566Allows the selection of one or more model objects, suitable for representing a 
     1567many-to-many relation. As with ``ModelChoiceField``, you can use 
     1568``label_from_instance`` to customize the object labels. 
    15611569 
    15621570Creating custom fields 
  • django/trunk/tests/modeltests/model_forms/models.py

    r7322 r7326  
    660660ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] 
    661661 
     662# check that we can safely iterate choices repeatedly 
     663>>> gen_one = list(f.choices) 
     664>>> gen_two = f.choices 
     665>>> gen_one[2] 
     666(2L, u"It's a test") 
     667>>> list(gen_two) 
     668[(u'', u'---------'), (1L, u'Entertainment'), (2L, u"It's a test"), (3L, u'Third')] 
     669 
     670# check that we can override the label_from_instance method to print custom labels (#4620) 
     671>>> f.queryset = Category.objects.all() 
     672>>> f.label_from_instance = lambda obj: "category " + str(obj) 
     673>>> list(f.choices) 
     674[(u'', u'---------'), (1L, 'category Entertainment'), (2L, "category It's a test"), (3L, 'category Third'), (4L, 'category Fourth')] 
    662675 
    663676# ModelMultipleChoiceField #################################################### 
     
    745758ValidationError: [u'Select a valid choice. 4 is not one of the available choices.'] 
    746759 
     760>>> f.queryset = Category.objects.all() 
     761>>> f.label_from_instance = lambda obj: "multicategory " + str(obj) 
     762>>> list(f.choices) 
     763[(1L, 'multicategory Entertainment'), (2L, "multicategory It's a test"), (3L, 'multicategory Third'), (4L, 'multicategory Fourth')] 
    747764 
    748765# PhoneNumberField ############################################################