- Author:
 - facundo_olano
 - Posted:
 - March 2, 2011
 - Language:
 - Python
 - Version:
 - 1.2
 - Score:
 - 0 (after 0 ratings)
 
This class makes easier the job of rendering lists of model instances in django templates. It's intended to mimic the behavior of the Model Forms in that it contains the code needed to render it as an HTML table and makes it easy to handle all the model lists from a single view (as it's usually done with the generic views for creating and updating model instances).
It also supports pagination and provides hooks for subclassing and customizing the rendered fields, column titles and list order.
Basic example:
class Account(Model):
   
name = models.CharField(max_length=MAX_LENGTH)
   
responsible = models.CharField(max_length=MAX_LENGTH)
   
email = models.EmailField()
class AccountModelList(ModelList):
   
class Meta:
   
model = Account
   
fields = ['name', 'responsible'] #email won't get a column
The model list would be instantiated with something like:
model_list = AccountModelList(instances=account_queryset)
Then a table header can be rendered with model_list.as_table_header(), while the table rows can be rendered calling as_table() on each model_list.items element.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193  | from django.forms.models import fields_for_model
from django.core.paginator import Paginator, EmptyPage, InvalidPage
from django.utils.datastructures import SortedDict
class ModelListItem(object):
    """ Item for a ModelList. It represent a model instance. """  
    
    def __init__(self, instance, fields = None, exclude=None):
        self.instance = instance
        self.fields = fields
        self.exclude = exclude    
    
    def as_table_row(self):
        """ 
        Returns the list item as a table row, where every field
        is a table cell. 
        """
        
        row = u"" 
        
        for field in self.as_field_list():
            row += u"<td>" + field + u"</td>"
        
        return row
    
    def as_field_list(self):
        """ Returns the list item as a list of pretty-printed values. """
        
        field_list = [] 
        
        for attr in fields_for_model(self.instance, self.fields, self.exclude).iterkeys():
            field_list.append(self.get_value(attr))
        
        return field_list
    
    def get_value(self, attr):
        """ 
        Returns a string representation of the value of the given field name.
        First tries to use a function get_<attr>_value, otherwise infers the
        value.
        Subclasses of this can define that function for specific display.
        """
        
        function_name = "get_" + attr + "_value"
        if hasattr(self, function_name):
            return getattr(self, function_name)(attr)
        
        #for choices
        choice_display = "get_" + attr + "_display" 
        if hasattr(self.instance, choice_display):
            return getattr(self.instance, choice_display)()
        
        value = getattr(self.instance, attr)
        if value is True:
            return u"Yes"
        elif value is False:
            return u"No"
        
        if value is None:
            return u""
        
        return unicode(value)
    
class ModelList(object):
    '''    
    An object representing a list of models to use in template rendering.
    It's analogous to the Django ModelForm.
    '''
    
    def __init__(self, instances=None, paginate_by=None, page=1, 
                 order_by=None):
        
        self.__complete_meta__()
        self.__override_parameters__(instances, paginate_by, order_by)
        
        self.items = [self.__new_item__(i) for i in self.Meta.instances]
        if self.Meta.order_by:
            self.__order__()
        
        if self.Meta.paginate_by:
            self.__paginate__(page)        
    
    
    def __new_item__(self, instance):
        """
        Given an instance to be added to the model list, a new model list item
        is created. Useful for overriding in subclasses to construct complex 
        list items.
        """
        
        return self.Meta.item_class(instance, self.Meta.fields, 
                                    self.Meta.exclude)
    
    def __complete_meta__(self):
        """
        This method ensures that the Meta class has all the needed attributes, 
        so only those to that change have to be overriden.
        """
        for field in dir(ModelList.Meta):
            if not hasattr(self.Meta, field):
                setattr(self.Meta, field, getattr(ModelList.Meta, field))
    
    def __override_parameters__(self, instances, paginate_by, order_by):
        if instances is not None:
            self.Meta.instances = instances
        
        if paginate_by is not None:
            self.Meta.paginate_by = paginate_by
        
        if order_by is not None:
            self.Meta.order_by = order_by
    
    def __paginate__(self, page):
        """
        Filters the queryset and returns the correct page object
        """
        paginator = Paginator(self.items, self.Meta.paginate_by)
        
        try:
            self.page_obj = paginator.page(page)
        except (EmptyPage, InvalidPage):
            self.page_obj = paginator.page(paginator.num_pages)
        
        self.items = self.page_obj.object_list
    
    def __order__(self):
        reverse = False
        
        if self.Meta.order_by.startswith('-'):
            reverse = True
            self.Meta.order_by = self.Meta.order_by[1:]
        
        try:
            self.items.sort(key=self.__key_function__, reverse=reverse)
        except AttributeError:
            #if the order_by is not a valid field, don't order
            pass
                   
    def __key_function__(self, item):
        
        value = item.get_value(self.Meta.order_by)
        
        if value.startswith(u'$'):
            value = value[1:]
        
        if value.endswith(u'%'):
            value = value[:len(value) - 1]
        
        try:
            return float(value)
        
        except ValueError:        
            return value.lower()
    
    def as_table_header(self):
        field_names = self.field_names()
        
        tds = [u'<td>' + field_names[field] + u'</td>' for field 
                                            in field_names.keys()]            
        return u''.join(tds)
    
    
    def field_names(self):
        model_fields = fields_for_model(self.Meta.model,
                                      self.Meta.fields, self.Meta.exclude)
        
        return SortedDict((field, self.get_name(field)) for field in model_fields)
    
    
    def get_name(self, field): 
        """
        Returns the name of the given field. First tries to use a function
        get_<field>_name, otherwise it uses the field label.
        """
        
        function_name = "get_" + field + "_name"
        if hasattr(self, function_name):
            return getattr(self, function_name)(field)        
        
        return field.capitalize()
    
        
    class Meta:
        model = None
        instances = []
        fields = None
        exclude = None 
        item_class = ModelListItem
        paginate_by = None
        order_by = None
 | 
More like this
- Add Toggle Switch Widget to Django Forms by OgliariNatan 1 month, 4 weeks ago
 - get_object_or_none by azwdevops 5 months, 2 weeks ago
 - Mask sensitive data from logger by agusmakmun 7 months, 2 weeks ago
 - Template tag - list punctuation for a list of items by shapiromatron 1 year, 9 months ago
 - JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 1 year, 9 months ago
 
Comments
Please login first before commenting.