1 from django.db import models
2 from django.contrib.contenttypes.models import ContentType
3 from django.core.paginator import Paginator, EmptyPage
4 from django.template import Context
5 from django.template.loader_tags import ExtendsNode, ConstantIncludeNode
8 def fattr(*args, **kwargs):
10 Returns a wrapper which takes a function as its only argument and sets the key/value pairs passed in with kwargs as attributes on that function. This can be used as a decorator.
14 >>> from philo.utils import fattr
15 >>> @fattr(short_description="Hello World!")
19 >>> x.short_description
23 def wrapper(function):
25 setattr(function, key, kwargs[key])
30 ### ContentTypeLimiters
33 class ContentTypeLimiter(object):
35 return models.Q(pk__in=[])
37 def add_to_query(self, query, *args, **kwargs):
38 query.add_q(self.q_object(), *args, **kwargs)
41 class ContentTypeRegistryLimiter(ContentTypeLimiter):
42 """Can be used to limit the choices for a :class:`ForeignKey` or :class:`ManyToManyField` to the :class:`ContentType`\ s which have been registered with this limiter."""
46 def register_class(self, cls):
47 """Registers a model class with this limiter."""
48 self.classes.append(cls)
50 def unregister_class(self, cls):
51 """Unregisters a model class from this limiter."""
52 self.classes.remove(cls)
56 for cls in self.classes:
58 if issubclass(cls, models.Model):
59 if not cls._meta.abstract:
60 contenttype = ContentType.objects.get_for_model(cls)
61 contenttype_pks.append(contenttype.pk)
64 return models.Q(pk__in=contenttype_pks)
67 class ContentTypeSubclassLimiter(ContentTypeLimiter):
69 Can be used to limit the choices for a :class:`ForeignKey` or :class:`ManyToManyField` to the :class:`ContentType`\ s for all non-abstract models which subclass the class passed in on instantiation.
71 :param cls: The class whose non-abstract subclasses will be valid choices.
72 :param inclusive: Whether ``cls`` should also be considered a valid choice (if it is a non-abstract subclass of :class:`models.Model`)
75 def __init__(self, cls, inclusive=False):
77 self.inclusive = inclusive
81 def handle_subclasses(cls):
82 for subclass in cls.__subclasses__():
84 if issubclass(subclass, models.Model):
85 if not subclass._meta.abstract:
86 if not self.inclusive and subclass is self.cls:
88 contenttype = ContentType.objects.get_for_model(subclass)
89 contenttype_pks.append(contenttype.pk)
90 handle_subclasses(subclass)
93 handle_subclasses(self.cls)
94 return models.Q(pk__in=contenttype_pks)
100 def paginate(objects, per_page=None, page_number=1):
102 Given a list of objects, return a (``paginator``, ``page``, ``objects``) tuple.
104 :param objects: The list of objects to be paginated.
105 :param per_page: The number of objects per page.
106 :param page_number: The number of the current page.
107 :returns tuple: (``paginator``, ``page``, ``objects``) where ``paginator`` is a :class:`django.core.paginator.Paginator` instance, ``page`` is the result of calling :meth:`Paginator.page` with ``page_number``, and objects is ``page.objects``. Any of the return values which can't be calculated will be returned as ``None``.
111 per_page = int(per_page)
112 except (TypeError, ValueError):
113 # Then either it wasn't set or it was set to an invalid value
114 paginator = page = None
116 # There also shouldn't be pagination if the list is too short. Try count()
117 # first - good chance it's a queryset, where count is more efficient.
119 if objects.count() <= per_page:
120 paginator = page = None
121 except AttributeError:
122 if len(objects) <= per_page:
123 paginator = page = None
126 return paginator, page, objects
130 paginator = Paginator(objects, per_page)
132 page_number = int(page_number)
137 page = paginator.page(page_number)
141 objects = page.object_list
143 return paginator, page, objects
146 ### Facilitating template analysis.
149 LOADED_TEMPLATE_ATTR = '_philo_loaded_template'
150 BLANK_CONTEXT = Context()
153 def get_extended(self):
154 return self.get_parent(BLANK_CONTEXT)
157 def get_included(self):
161 # We ignore the IncludeNode because it will never work in a blank context.
162 setattr(ExtendsNode, LOADED_TEMPLATE_ATTR, property(get_extended))
163 setattr(ConstantIncludeNode, LOADED_TEMPLATE_ATTR, property(get_included))