class LightweightMixin(object):
    """ Manager mixin for lightweight functionality. """
    # heavy fields to defer in lightweight querysets
    heavyweight_fields = ()
    # relations that are always fetched with lightweight queries
    always_related_models = {}

    def as_public(self, qs, **kwargs):
        """ Apply visibility filter and relation prefetch on given QuerySet/Batch. """
        return qs

    def public(self, **kwargs):
        """ Returns default queryset with `as_public()` applied. """
        return self.as_public(self.all(), **kwargs)

    def lightweight(self, select=None, relations=None, **kwargs):
        """ Returns public() queryset with `as_lightweight()` applied. """
        return self.as_lightweight(self.public(**kwargs), select=select, relations=relations)

    def as_lightweight(self, qs, prefix='', select=None, relations=None):
        """
            Defer heavy-weight fields in given queryset.

            By default this method defers heavyweight fields in all relations, which
            are specified in `always_related_models`. Set `relations` to `False`
            to disable this behavior. You may override default relations by
            passing `dict` to `relations` argument.

            :Arguments:
                - `qs` (`QuerySet`): queryset to defer
                - `prefix` (str): object prefix, used in recursive defers
                - `select` (list of str): heavy fields that will be not defered
                - `relations` (dict or bool): recursively apply itself to relations.
        """
        defer = []
        select = set(select) if select else ()
        for fld in self.heavyweight_fields:
            pat = "%s%s" % (prefix, fld)
            if pat not in select:
                defer.append(pat)

        qs = qs.defer(*defer)
        if relations is not False: # by default always applies with default relations
            qs = self.as_lightweight_relations(qs, prefix=prefix, select=select, relations=relations)

        return qs

    def as_lightweight_relations(self, qs, prefix='', select=None, relations=None):
        """ Propogate defer calls to relations. """
        relations = relations or self.always_related_models
        for fld, model in relations.iteritems():
            relpref = "%s%s__" % (prefix, fld)
            qs = model.objects.as_lightweight(qs, relpref, select=select)

        return qs