I\'m trying to add some extra attributes to the elements of a QuerySet so I can use the extra information in templates, instead of hitting the database multiple times. Let m
Here is my version of alter_items
proposed by Will Hardy.
Instead of a single value it allows different values of a custom attribute for each object: you can pass a mapping of values by object id.
It also automatically wraps the results of all methods that return QuerySet
.
import types
from itertools import islice
from django.db.models import Model, QuerySet
class QuerySetWithCustomAttributes(object):
def __init__(self, queryset, **custom_attrs):
self.queryset = queryset
self.custom_attrs = custom_attrs
def __set_custom_attrs(self, obj):
if isinstance(obj, Model):
for key, val in self.custom_attrs.items():
setattr(obj, key, val[obj.id])
return obj
def __iter__(self):
for obj in self.queryset:
yield self.__set_custom_attrs(obj)
def __getitem__(self, ndx):
if type(ndx) is slice:
return self.__class__(self.queryset.__getitem__(ndx), **self.custom_attrs)
else:
return self.__set_custom_attrs(next(islice(self.queryset, ndx, ndx + 1)))
def __len__(self):
return len(self.queryset)
def __apply(self, method):
def apply(*args, **kwargs):
result = method(*args, **kwargs)
if isinstance(result, QuerySet):
result = self.__class__(result, **self.custom_attrs)
elif isinstance(result, Model):
self.__set_custom_attrs(result)
return result
return apply
def __getattr__(self, name):
attr = getattr(self.queryset, name)
if isinstance(attr, types.MethodType):
return self.__apply(attr)
else:
return attr