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