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"
" + field + u" | "
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__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'' + field_names[field] + u' | ' 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__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