Exemple #1
0
def home(request, model="s", objectId="0", page_num=1, template='home.html'):
	paginate_by = settings.PAGINATE_BY
	#what we get as parameter is always a string
	page_num = int(page_num)
	if model == 'u': #for user we need to filter for the user
		info_list = Paginator(sol.objects.filter(author__username=objectId), paginate_by)
	elif model== 's': #for sol; home page
                if objectId == '0':
                        info_list = Paginator(sol.objects.all(), paginate_by)
                else:
                        info_list = Paginator(sol.objects.filter(id=objectId), paginate_by)
	elif model =='g': #for group
		if objectId == '0':
			info_list = Paginator(group.objects.all(), paginate_by)
		else:
			info_list = Paginator(sol.objects.filter(group=group.objects.get(id=objectId)), paginate_by)

    #if the user altered the URL for a particular page that doesn't exist
	try:
		page_info = info_list.page(page_num)
	except InvalidPage:
		page_num = 1
		page_info = info_list.page(page_num)

	has_previous = page_info.has_previous()
	has_next = page_info.has_next()



	info_dict = {
		'query_list' : page_info.object_list,
		'has_previous' : page_info.has_previous(),
		'previous_page' : page_info.previous_page_number(),
		'has_next' : page_info.has_next(),
		'next_page' : page_info.next_page_number(),
		'site_name' : settings.SITE_NAME,
		'user' : request.user,
	}

	if model == 's':
		form = solForm()
		#this is how you append to a dict
		info_dict['solForm'] = form

	if model == 'u':
		#this is how you append to a dict
		info_dict['u_id'] = objectId
		info_dict['nickname'] = userprofile.objects.get(user__username=objectId).nickname

	if model == 'g':
		info_dict['grpForm'] = grpForm()
		if objectId == '0':
			info_dict['solForm'] = solForm()
		else:
			info_dict['solForm'] = solForm(initial={'group': group.objects.get(id=objectId)})
                        info_dict['grpId'] = objectId
			info_dict['grpName'] = group.objects.get(id=objectId).desc

	return render_to_response(template, info_dict)
Exemple #2
0
    def list(
        self,
        request,
        queryset,
        page=None,
        ):
        """
        Renders a list of model objects to HttpResponse.
        """

        template_name = '%s/%s_list.html' % (self.template_dir,
                queryset.model._meta.module_name)
        if self.paginate_by:
            paginator = QuerySetPaginator(queryset, self.paginate_by)
            if not page:
                page = request.GET.get('page', 1)
            try:
                page = int(page)
                object_list = paginator.page(page).object_list
            except (InvalidPage, ValueError):
                if page == 1 and self.allow_empty:
                    object_list = []
                else:
                    raise Http404
            current_page = paginator.page(page)
            c = RequestContext(request, {
                '%s_list' % self.template_object_name: object_list,
                'is_paginated': paginator.num_pages > 1,
                'results_per_page': self.paginate_by,
                'has_next': current_page.has_next(),
                'has_previous': current_page.has_previous(),
                'page': page,
                'next': page + 1,
                'previous': page - 1,
                'last_on_page': current_page.end_index(),
                'first_on_page': current_page.start_index(),
                'pages': paginator.num_pages,
                'hits': paginator.count,
                }, self.context_processors)
        else:
            object_list = queryset
            c = RequestContext(request, {'%s_list' \
                               % self.template_object_name: object_list,
                               'is_paginated': False},
                               self.context_processors)
            if not self.allow_empty and len(queryset) == 0:
                raise Http404

        # Hide unexposed fields

       
        for obj in object_list:
            self._hide_unexposed_fields(obj, self.expose_fields)
        c.update(self.extra_context)
       
        t = self.template_loader.get_template(template_name)
        return HttpResponse(t.render(c), mimetype=self.mimetype)
Exemple #3
0
def index(request, page=1):
    "Home page with pagination. Adapted to the trunk version of paginator"
    newurl = ''
    
    #cgi.parse_qsl(request)
    if (len(request.GET)>0) or (len(request.META['PATH_INFO']) >0):
        if request.GET:
            search_form = SearchForm(request.GET)
        else:
            values = {}
            for k,v in cgi.parse_qs(request.META['PATH_INFO'], True).items():
                if v[0] != 'None':
                    values[k] = v[0]
            search_form = SearchForm(values)

        if search_form.is_valid():
            searchdict = search_form.cleaned_data
            qdict = { 'first_name': 'first_name__icontains',
                      'last_name': 'last_name__icontains',
                      'age': 'age'}
            q_objs = [Q(**{qdict[k]: searchdict[k]}) for k in qdict.keys() if searchdict.get(k, None)]
            results = Person.objects.select_related().filter(*q_objs).order_by('first_name')

            # Encode the GET data to a URL so we can append it to the next
            # and previous page links.
            rawurl = urllib.urlencode(searchdict)
            if len(rawurl):
                newurl = '&' + rawurl
    else:
        results = Person.objects.all()
        search_form = SearchForm()

    data = dict()
    actual_page = int(page)
    paginator = QuerySetPaginator(results,RECORDS_PER_PAGE)
    try:
        data['page'] = paginator.page(int(page))
    except EmptyPage:
        data['page'] = paginator.page(1)
        actual_page = 1
    data['paginator'] = paginator
    data['url']='/agenda/list/page/'
    #numbre of pages you want visible    
    if actual_page+VISIBLE_PAGES-1 <= paginator.num_pages:
        start_range = actual_page
    else:
        start_range = max(paginator.num_pages - VISIBLE_PAGES +1,1)
    end_range= min(start_range+VISIBLE_PAGES-1, paginator.num_pages)
    data['range'] = range(start_range,end_range)
    data['end_range'] = end_range
    data['form'] = search_form
    data['newurl'] = newurl
    return  render_to_response('agenda/index.html',data, context_instance=RequestContext(request))
Exemple #4
0
 def list(self, request, queryset, page=None):
     """
     Renders a list of model objects to HttpResponse.
     """
     template_name = '%s/%s_list.html' % (self.template_dir,
                                          queryset.model._meta.module_name)
     if self.paginate_by:
         paginator = QuerySetPaginator(queryset, self.paginate_by)
         if not page:
             page = request.GET.get('page', 1)
         try:
             page = int(page)
             object_list = paginator.page(page).object_list
         except (InvalidPage, ValueError):
             if page == 1 and self.allow_empty:
                 object_list = []
             else:
                 raise Http404
         current_page = paginator.page(page)
         c = RequestContext(
             request, {
                 '%s_list' % self.template_object_name: object_list,
                 'is_paginated': paginator.num_pages > 1,
                 'results_per_page': self.paginate_by,
                 'has_next': current_page.has_next(),
                 'has_previous': current_page.has_previous(),
                 'page': page,
                 'next': page + 1,
                 'previous': page - 1,
                 'last_on_page': current_page.end_index(),
                 'first_on_page': current_page.start_index(),
                 'pages': paginator.num_pages,
                 'hits': paginator.count,
             }, self.context_processors)
     else:
         object_list = queryset
         c = RequestContext(
             request, {
                 '%s_list' % self.template_object_name: object_list,
                 'is_paginated': False
             }, self.context_processors)
         if not self.allow_empty and len(queryset) == 0:
             raise Http404
     # Hide unexposed fields
     for obj in object_list:
         self._hide_unexposed_fields(obj, self.expose_fields)
     c.update(self.extra_context)
     t = self.template_loader.get_template(template_name)
     return HttpResponse(t.render(c), mimetype=self.mimetype)
Exemple #5
0
def paginate_queryset_for_request(request, qset, paginate_by):
    """ returns appropriate page for view. Page number should
        be set in GET variable 'p', if not set first page is returned.
    """
    item_number_mapping = {}
    for i, c in enumerate(qset):
        item_number_mapping[c._get_pk_val()] = i + 1
    paginator = QuerySetPaginator(qset, paginate_by)
    page_no = request.GET.get('p', paginator.page_range[0])
    try:
        page_no = int(page_no)
        if not page_no in paginator.page_range:
            page_no = paginator.page_range[0]
    except Exception:
        page_no = paginator.page_range[0]
    context = {}
    page = paginator.page(page_no)
    objs = page.object_list
    context['object_list'] = objs
    context.update({
        'is_paginated': paginator.num_pages > 1,
        'results_per_page': paginate_by,
        'page': page,
        'item_number_mapping': item_number_mapping,
    })
    return context
Exemple #6
0
def series_and_issue(request, series_name, issue_nr, sort=ORDER_ALPHA):
    """ Looks for issue_nr in series_name """
    things = Issue.objects.filter(series__name__exact = series_name) \
                .filter(number__exact = issue_nr)
    
    if things.count() == 1: # if one display the issue
        return HttpResponseRedirect(urlresolvers.reverse(issue,
                                    kwargs={ 'issue_id': things[0].id }))
    else: # if more or none use issue_list.html from search
        p = QuerySetPaginator(things, 100)
        page_num = 1
        if (request.GET.has_key('page')):
            page_num = int(request.GET['page'])
        page = p.page(page_num)
        
        context = {
            'items' : things,
            'item_name' : 'issue',
            'plural_suffix' : 's',
            'heading' : series_name + ' #' + issue_nr,
            'style' : 'default',
        }
        if 'style' in request.GET:
            context['style'] = request.GET['style']

        return paginate_response(
          request, things, 'gcd/search/issue_list.html', context)
Exemple #7
0
def detail(request, context):
    """ Custom object detail function that adds a QuestionForm to the context. """
    interview = context['object']
    page_no = get_page_no(request)
    qset = interview.get_questions()
    paginator = QuerySetPaginator(qset,
                                  interviews_settings.PAGINATION_PER_PAGE)

    if page_no > paginator.num_pages or page_no < 1:
        raise Http404

    page = paginator.page(page_no)

    interviewees = interview.get_interviewees(request.user)
    context.update({
        'interviewees': interviewees,
        'is_paginated': paginator.num_pages > 1,
        'results_per_page': interviews_settings.PAGINATION_PER_PAGE,
        'page': page,
        'form': QuestionForm(request),
        'questions': page.object_list,
    })

    return render_to_response(get_templates_from_placement(
        'object.html', context['placement']),
                              context,
                              context_instance=RequestContext(request))
Exemple #8
0
def paginate_queryset_for_request(request, qset, paginate_by):
    """ returns appropriate page for view. Page number should
        be set in GET variable 'p', if not set first page is returned.
    """
    item_number_mapping = {}
    for i, c in enumerate(qset):
        item_number_mapping[c._get_pk_val()] = i + 1
    paginator = QuerySetPaginator(qset, paginate_by)
    page_no = request.GET.get('p', paginator.page_range[0])
    try:
        page_no = int(page_no)
        if not page_no in paginator.page_range:
            page_no = paginator.page_range[0]
    except Exception:
        page_no = paginator.page_range[0]
    context = {}
    page = paginator.page(page_no)
    objs = page.object_list
    context['object_list'] = objs
    context.update({
        'is_paginated': paginator.num_pages > 1,
        'results_per_page': paginate_by,
        'page': page,
        'item_number_mapping': item_number_mapping,
})
    return context
def search_results(request, service_id, search_terms, page_number=1):
    results = DownloadLink.objects.filter(hidden=False)

    term_list = search_terms.split('+')

    for term in term_list:
        results = results.filter(name__icontains=term)

    try:
        service = DownloadService.objects.get(id=service_id)
        results = results.filter(service=service)
    except:
        service = None

    paginator = QuerySetPaginator(results, 25)
    page = paginator.page(page_number)

    return render_response(
        request, 'style2/search_results.html', '', {
            'search_terms': search_terms,
            'service': service,
            'page': page,
            'paginator': paginator,
            'term_list': term_list,
        })
Exemple #10
0
def detail(request, context):
    """ Custom object detail function that adds a QuestionForm to the context. """
    interview = context['object']
    page_no = get_page_no(request)
    qset = interview.get_questions()
    paginator = QuerySetPaginator(qset, INTERVIEW_PAGINATION_PER_PAGE)

    if page_no > paginator.num_pages or page_no < 1:
        raise Http404

    page = paginator.page(page_no)

    interviewees = interview.get_interviewees(request.user)
    context.update({
        'interviewees': interviewees,
        'is_paginated': paginator.num_pages > 1,
        'results_per_page': INTERVIEW_PAGINATION_PER_PAGE,
        'page': page,
        'form' : QuestionForm(request=request),
        'questions' : page.object_list,
})

    return render_to_response(
        get_templates_from_placement('object.html', context['placement']),
        context,
        context_instance=RequestContext(request)
)
Exemple #11
0
    def list(
        self,
        request,
        queryset,
        page=None,
        ):
        """
        Renders a list of model objects to HttpResponse.
        """

        if self.paginate_by:
            paginator = QuerySetPaginator(queryset, self.paginate_by)
            if not page:
                page = request.GET.get('page', 1)
            try:
                page = int(page)
                object_list = paginator.page(page).object_list
            except (InvalidPage, ValueError):
                if page == 1 and self.allow_empty:
                    object_list = []
                else:
                    return self.error(request, 404)
        else:
            object_list = list(queryset)
        return HttpResponse(self.render(object_list), self.mimetype)
Exemple #12
0
def search(request):
    import re
    from pysolr import Solr
    from stats.models import DailySearch
    from settings import SOLR_URL

    def _fail(query):
        # phrase is not changed, query is normalized phrase
        return render_to_response('search_results.html', {
            'result': [],
            'query': query,
            'phrase': query,
        },
                                  context_instance=RequestContext(request))

    phrase = request.GET.get('phrase')
    try:
        conn = Solr(SOLR_URL)
    except:
        return _fail(phrase)
    result = []
    if not phrase:
        raise Http404("Malformed request.")
    q = phrase
    if phrase.startswith('*') or phrase.startswith('?'):
        q = phrase[1:]
    q = q.strip()
    q = re.sub('[' + '\[<>@\]' + ']', '', q)
    q = re.sub('`', '"', q)
    q = re.sub('\s*:', ':', q)
    q = re.sub(
        '(?<!author)(?<!title)(?<!text)(?<!file)(?<!tag)(?<!artist)(?<!album)(?<!year)(?<!company)(?<!created):',
        ' ', q)
    if not q:
        return _fail(phrase)
    results = conn.search(q)
    if not results:
        return _fail(q)
    ids = [i['id'] for i in results]
    result = QuerySetPaginator(Topic.objects.filter(pk__in=ids),
                               RESULTS_ON_PAGE,
                               orphans=5)
    if result.num_pages == 0:
        return _fail(q)
    p = DailySearch.objects.create(phrase=q.strip())
    page = request.GET.get('page', 1)
    try:
        page = int(page)
        r = result.page(page)
    except (InvalidPage, ValueError):
        raise Http404("No such page")
    return render_to_response('search_results.html', {
        'result': r,
        'query': q,
        'phrase': phrase,
        'page': int(page),
        'title': phrase,
    },
                              context_instance=RequestContext(request))
def user_list(request):
    queryset = Timeline.objects.filter(user__is_active=True).values("user").annotate(last=Max("time")).order_by("-last")
    if request.current_user:
        ctype = ContentType.objects.get_for_model(User)
        favorites = Favorite.objects.filter(user=request.current_user, content_type=ctype).values_list(
            "object_id", flat=True
        )
        queryset = queryset.filter(user__id__in=favorites)
    paginator = QuerySetPaginator(queryset, 20)
    try:
        page = paginator.page(request.GET.get("p", 1))
    except (EmptyPage, InvalidPage):
        page = paginator.page(paginator.num_pages)
    return render_to_response(
        "timeline/user_list.html",
        {"user_list": [User.objects.get(id=item["user"]) for item in page.object_list], "page": page},
        context_instance=RequestContext(request),
    )
Exemple #14
0
def make_pages(querySet, items_at_page=20, current_page=None):
    pages = QuerySetPaginator(querySet, items_at_page)

    page_number = validate_page_number(current_page, pages.num_pages)
    posts = pages.page(page_number).object_list
    context = {'items': posts}

    context.update(other_pages(page_number, pages.num_pages))
    return context
def browse2(request, page_number=1):
    links = DownloadLink.objects.filter(hidden=False)

    paginator = QuerySetPaginator(links, 25)
    page = paginator.page(page_number)

    return render_response(request, 'style2/link_browse_json.html', '', {
        'page': page,
        'paginator': paginator,
    })
Exemple #16
0
def surveygroup_list(request):

    page_no = int(request.GET.get('page', 1))

    surveygroup_list = SurveyGroup.objects.all()

    p = QuerySetPaginator(surveygroup_list, 50)
    page = p.page(page_no)

    return render_to_response('django_surveys/surveygroup_list.html', locals(), context_instance=RequestContext(request)) 
Exemple #17
0
def survey_list(request, surveygroup_id):

    surveygroup = get_object_or_404(SurveyGroup, pk=surveygroup_id)
    survey_list = Survey.objects.filter(survey_group__id=surveygroup_id)
    page_no = int(request.GET.get('page', 1))

    p = QuerySetPaginator(survey_list, 50)
    page = p.page(page_no)


    return render_to_response('django_surveys/survey_list.html', locals(), context_instance=RequestContext(request))
def link_topic(request, topic_id, page_number=1):
    topic = get_object_or_404(DownloadTopics, pk=topic_id)
    links = DownloadLink.objects.filter(hidden=False, from_topic=topic)

    paginator = QuerySetPaginator(links, 26)
    page = paginator.page(page_number)

    return render_response(request, 'style2/link_topic.html', '', {
        'topic': topic,
        'page': page,
        'paginator': paginator,
    })
Exemple #19
0
def surveygroup_list(request):

    page_no = int(request.GET.get('page', 1))

    surveygroup_list = SurveyGroup.objects.all()

    p = QuerySetPaginator(surveygroup_list, 50)
    page = p.page(page_no)

    return render_to_response('django_surveys/surveygroup_list.html',
                              locals(),
                              context_instance=RequestContext(request))
Exemple #20
0
def survey_list(request, surveygroup_id):

    surveygroup = get_object_or_404(SurveyGroup, pk=surveygroup_id)
    survey_list = Survey.objects.filter(survey_group__id=surveygroup_id)
    page_no = int(request.GET.get('page', 1))

    p = QuerySetPaginator(survey_list, 50)
    page = p.page(page_no)

    return render_to_response('django_surveys/survey_list.html',
                              locals(),
                              context_instance=RequestContext(request))
def search(request):
    import re
    from pysolr import Solr
    from stats.models import DailySearch
    from settings import SOLR_URL
    def _fail(query):
	# phrase is not changed, query is normalized phrase
	return render_to_response('search_results.html', {
	    'result': [],
	    'query': query,
	    'phrase': query,
	}, context_instance=RequestContext(request))
    phrase = request.GET.get('phrase')
    try:
	conn = Solr(SOLR_URL)
    except:
	return _fail(phrase)
    result = []
    if not phrase:
	raise Http404("Malformed request.")
    q = phrase
    if phrase.startswith('*') or phrase.startswith('?'):
	q = phrase[1:]
    q = q.strip()
    q = re.sub('['+'\[<>@\]'+']', '', q)
    q = re.sub('`', '"', q)
    q = re.sub('\s*:',':', q)
    q = re.sub('(?<!author)(?<!title)(?<!text)(?<!file)(?<!tag)(?<!artist)(?<!album)(?<!year)(?<!company)(?<!created):', ' ', q)
    if not q:
	return _fail(phrase)
    results = conn.search(q)
    if not results:
	return _fail(q)
    ids = [i['id'] for i in results]
    result = QuerySetPaginator(Topic.objects.filter(pk__in=ids), RESULTS_ON_PAGE, orphans=5)
    if result.num_pages == 0:
	return _fail(q)
    p = DailySearch.objects.create(phrase=q.strip())
    page = request.GET.get('page', 1)
    try:
	page = int(page)
	r = result.page(page)
    except (InvalidPage, ValueError):
	raise Http404("No such page")
    return render_to_response('search_results.html', {
	'result': r,
	'query': q,
	'phrase': phrase,
	'page': int(page),
	'title': phrase,
    }, context_instance=RequestContext(request))
Exemple #22
0
def service_listing(request, service_id, page_number=1):
    service = get_object_or_404(DownloadService, pk=service_id)

    links = service.downloadlink_set.filter(hidden=False)

    paginator = QuerySetPaginator(links, 25)
    page = paginator.page(page_number)

    return render_response(request, 'style2/service_view.html', '/service/%s/' % service_id,
        {
        'service': service,
        'page': page,
        'paginator': paginator,
        })
Exemple #23
0
 def list(self, request, queryset, page=None):
     """
     Renders a list of model objects to HttpResponse.
     """
     if self.paginate_by:
         paginator = QuerySetPaginator(queryset, self.paginate_by)
         if not page:
             page = request.GET.get('page', 1)
         try:
             page = int(page)
             object_list = paginator.page(page).object_list
         except (InvalidPage, ValueError):
             if page == 1 and self.allow_empty:
                 object_list = []
             else:
                 return self.error(request, 404)
     else:
         object_list = list(queryset)
     return HttpResponse(self.render(object_list), self.mimetype)
Exemple #24
0
    def get_results(self, request):
        paginator = QuerySetPaginator(self.query_set,
                                      self.lookup_opts.admin.list_per_page)

        # Get the number of objects, with admin filters applied.
        try:
            result_count = paginator.count
        # Naked except! Because we don't have any other way of validating
        # "params". They might be invalid if the keyword arguments are
        # incorrect, or if the values are not in the correct type (which would
        # result in a database error).
        except:
            raise IncorrectLookupParameters

        # Get the total number of objects, with no admin filters applied.
        # Perform a slight optimization: Check to see whether any filters were
        # given. If not, use paginator.hits to calculate the number of objects,
        # because we've already done paginator.hits and the value is cached.
        if isinstance(self.query_set._filters,
                      models.Q) and not self.query_set._filters.kwargs:
            full_result_count = result_count
        else:
            full_result_count = self.manager.count()

        can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
        multi_page = result_count > self.lookup_opts.admin.list_per_page

        # Get the list of objects to display on this page.
        if (self.show_all and can_show_all) or not multi_page:
            result_list = list(self.query_set)
        else:
            try:
                result_list = paginator.page(self.page_num + 1).object_list
            except InvalidPage:
                result_list = ()

        self.result_count = result_count
        self.full_result_count = full_result_count
        self.result_list = result_list
        self.can_show_all = can_show_all
        self.multi_page = multi_page
        self.paginator = paginator
Exemple #25
0
def post_list(request):
    page_no = request.GET.get('page', 1)

    if page_no == 1:
        cached_page = cache.get('/')
        if cached_page:
            return cached_page

    paginator = QuerySetPaginator(Post.objects.all(), 10)
    context = {
        'paginator': paginator,
        'page': paginator.page(page_no),
    }
    response = render_to_response('blog/post_list.html', context,
        RequestContext(request))

    if page_no == 1:
        cache.set('/', response)

    return response
Exemple #26
0
def render(request, template, dict={}):
    r = Result.objects.all().order_by('-run_date', '-upload_date')
    p = QuerySetPaginator(r, 50)

    try:
        page_num = int(request.GET['p'])
        if page_num < 1:
            page_num = 1
        elif page_num > p.num_pages:
            page_num = p.num_pages
    except:
        page_num = 1

    page = p.page(page_num)

    dict['p'] = p
    dict['page'] = page
    dict['results'] = result_list(page.object_list)
    dict['r'] = result_list(r)

    return render_to_response(template, dict)
Exemple #27
0
    def get_results(self, request):
        paginator = QuerySetPaginator(self.query_set, self.lookup_opts.admin.list_per_page)

        # Get the number of objects, with admin filters applied.
        try:
            result_count = paginator.count
        # Naked except! Because we don't have any other way of validating
        # "params". They might be invalid if the keyword arguments are
        # incorrect, or if the values are not in the correct type (which would
        # result in a database error).
        except:
            raise IncorrectLookupParameters

        # Get the total number of objects, with no admin filters applied.
        # Perform a slight optimization: Check to see whether any filters were
        # given. If not, use paginator.hits to calculate the number of objects,
        # because we've already done paginator.hits and the value is cached.
        if isinstance(self.query_set._filters, models.Q) and not self.query_set._filters.kwargs:
            full_result_count = result_count
        else:
            full_result_count = self.manager.count()

        can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
        multi_page = result_count > self.lookup_opts.admin.list_per_page

        # Get the list of objects to display on this page.
        if (self.show_all and can_show_all) or not multi_page:
            result_list = list(self.query_set)
        else:
            try:
                result_list = paginator.page(self.page_num+1).object_list
            except InvalidPage:
                result_list = ()

        self.result_count = result_count
        self.full_result_count = full_result_count
        self.result_list = result_list
        self.can_show_all = can_show_all
        self.multi_page = multi_page
        self.paginator = paginator
def list(request, page=0):
    comments = FreeComment.objects.filter(content_type=Photo, approved=True, is_public=True)

    paginator = QuerySetPaginator(comments, 25, allow_empty_first_page=True)
    if not page:
        page = request.GET.get("page", 1)
        try:
            page_number = int(page)
        except ValueError:
            if page == "last":
                page_number = paginator.num_pages
            else:
                raise Http404

        try:
            page_obj = paginator.page(page_number)
        except InvalidPage:
            raise Http404

    return render(
        request=request,
        template_name="photos/comment_list.html",
        payload={
            "comments": comments,
            "is_paginated": page_obj.has_other_pages(),
            "results_per_page": paginator.per_page,
            "has_next": page_obj.has_next(),
            "has_previous": page_obj.has_previous(),
            "page": page_obj.number,
            "next": page_obj.next_page_number(),
            "previous": page_obj.previous_page_number(),
            "first_on_page": page_obj.start_index(),
            "last_on_page": page_obj.end_index(),
            "pages": paginator.num_pages,
            "hits": paginator.count,
            "page_range": paginator.page_range,
        },
    )
def list(request, page=0):
    comments = FreeComment.objects.filter(content_type=Photo,
                                          approved=True,
                                          is_public=True)

    paginator = QuerySetPaginator(comments, 25, allow_empty_first_page=True)
    if not page:
        page = request.GET.get('page', 1)
        try:
            page_number = int(page)
        except ValueError:
            if page == 'last':
                page_number = paginator.num_pages
            else:
                raise Http404

        try:
            page_obj = paginator.page(page_number)
        except InvalidPage:
            raise Http404

    return render(request=request,
                  template_name='photos/comment_list.html',
                  payload={
                      'comments': comments,
                      'is_paginated': page_obj.has_other_pages(),
                      'results_per_page': paginator.per_page,
                      'has_next': page_obj.has_next(),
                      'has_previous': page_obj.has_previous(),
                      'page': page_obj.number,
                      'next': page_obj.next_page_number(),
                      'previous': page_obj.previous_page_number(),
                      'first_on_page': page_obj.start_index(),
                      'last_on_page': page_obj.end_index(),
                      'pages': paginator.num_pages,
                      'hits': paginator.count,
                      'page_range': paginator.page_range,
                  })
def detail(request, gallery_slug, page=0):
	gallery = get_object_or_404(Gallery, slug__iexact=gallery_slug)
	photos = Photo.objects.filter(gallery=gallery)
	
	paginator = QuerySetPaginator(photos, 25, allow_empty_first_page=True)
	if not page:
		page = request.GET.get('page', 1)
		try:
			page_number = int(page)
		except ValueError:
			if page == 'last':
				page_number = paginator.num_pages
			else:
				raise Http404
		
		try:
			page_obj = paginator.page(page_number)
		except InvalidPage:
			raise Http404
	
	return render(request, 'photos/gallery_detail.html', {
		'gallery':			gallery,
		'photos':			photos,
		'is_paginated':		page_obj.has_other_pages(),
		'results_per_page':	paginator.per_page,
		'has_next':			page_obj.has_next(),
		'has_previous':		page_obj.has_previous(),
		'page':				page_obj.number,
		'next':				page_obj.next_page_number(),
		'previous':			page_obj.previous_page_number(),
		'first_on_page':	page_obj.start_index(),
		'last_on_page':		page_obj.end_index(),
		'pages':			paginator.num_pages,
		'hits':				paginator.count,
		'page_range':		paginator.page_range,
	})
def search_results(request, service_id, search_terms, page_number=1):
    results = DownloadLink.objects.filter(hidden=False)

    term_list = search_terms.split('+')

    for term in term_list:
        results = results.filter(name__icontains=term)

    try:
        service = DownloadService.objects.get(id=service_id)
        results = results.filter(service=service)
    except:
        service = None

    paginator = QuerySetPaginator(results, 25)
    page = paginator.page(page_number)

    return render_response(request, 'style2/search_results.html', '', {
        'search_terms': search_terms,
        'service': service,
        'page': page,
        'paginator': paginator,
        'term_list': term_list,
        })
Exemple #32
0
def archive_index(request, queryset, date_field, num_latest=15,
        page=None, template_name=None, template_loader=loader,
        extra_context=None, allow_empty=True, context_processors=None,
        mimetype=None, allow_future=False, template_object_name='latest'):
    """
    Generic top-level archive of date-based objects.
 
    A replacement for ``django.views.generic.date_based.archive_index``
    which offers result-set pagination.
 
    Templates: ``<app_label>/<model_name>_archive.html``
    Context:
        date_list
            List of years
        latest
            Latest N (defaults to 15) objects by date
        paginator
           ``django.core.paginator.Paginator`` object
           (if ``num_latest`` provided)
        page_obj
           ``django.core.paginator.Page`` object
           (if ``num_latest`` provided)
    """
    if extra_context is None: extra_context = {}
    model = queryset.model
    if not allow_future:
        queryset = queryset.filter(**{'%s__lte' % date_field: datetime.datetime.now()})
    date_list = queryset.dates(date_field, 'year')[::-1]
    if not date_list and not allow_empty:
        raise Http404, "No %s available" % model._meta.verbose_name
 
    if date_list and num_latest:
        latest = queryset.order_by('-'+date_field)
        paginator = QuerySetPaginator(latest, per_page=num_latest, allow_empty_first_page=allow_empty)
        if not page:
            page = request.GET.get('page', 1)
        try:
            page_number = int(page)
        except ValueError:
            if page == 'last':
                page_number = paginator.num_pages
            else:
                raise Http404
        try:
            page_obj = paginator.page(page_number)
        except InvalidPage:
            raise Http404
        c = RequestContext(request, {
            'date_list': date_list,
            template_object_name: page_obj.object_list,
            'paginator': paginator,
            'page_obj': page_obj,
        }, context_processors)
    else:
        latest = None
        c = RequestContext(request, {
            'date_list' : date_list,
            template_object_name : latest,
            'paginator': None,
            'page_obj': None,
        }, context_processors)
    if not template_name:
        template_name = "%s/%s_archive.html" % (model._meta.app_label, model._meta.object_name.lower())
    t = template_loader.get_template(template_name)
 
    for key, value in extra_context.items():
        if callable(value):
            c[key] = value()
        else:
            c[key] = value
    return HttpResponse(t.render(c), mimetype=mimetype)
Exemple #33
0
class DataGrid(object):
    """
    A representation of a list of objects, sorted and organized by
    columns. The sort order and column lists can be customized. allowing
    users to view this data however they prefer.

    This is meant to be subclassed for specific uses. The subclasses are
    responsible for defining one or more column types. It can also set
    one or more of the following optional variables:

        * 'title':                  The title of the grid.
        * 'profile_sort_field':     The variable name in the user profile
                                    where the sort order can be loaded and
                                    saved.
        * 'profile_columns_field":  The variable name in the user profile
                                    where the columns list can be loaded and
                                    saved.
        * 'paginate_by':            The number of items to show on each page
                                    of the grid. The default is 50.
        * 'paginate_orphans':       If this number of objects or fewer are
                                    on the last page, it will be rolled into
                                    the previous page. The default is 3.
        * 'page':                   The page to display. If this is not
                                    specified, the 'page' variable passed
                                    in the URL will be used, or 1 if that is
                                    not specified.
        * 'listview_template':      The template used to render the list view.
                                    The default is 'datagrid/listview.html'
        * 'column_header_template': The template used to render each column
                                    header. The default is
                                    'datagrid/column_header.html'
        * 'cell_template':          The template used to render a cell of
                                    data. The default is 'datagrid/cell.html'
        * 'optimize_sorts':         Whether or not to optimize queries when
                                    using multiple sorts. This can offer a
                                    speed improvement, but may need to be
                                    turned off for more advanced querysets
                                    (such as when using extra()).
                                    The default is True.
    """
    def __init__(self, request, queryset=None, title="", extra_context={},
                 optimize_sorts=True):
        self.request = request
        self.queryset = queryset
        self.rows = []
        self.columns = []
        self.all_columns = []
        self.db_field_map = {}
        self.id_list = []
        self.paginator = None
        self.page = None
        self.sort_list = None
        self.state_loaded = False
        self.page_num = 0
        self.id = None
        self.extra_context = dict(extra_context)
        self.optimize_sorts = optimize_sorts
        self.cell_template_obj = None
        self.column_header_template_obj = None

        if not hasattr(request, "datagrid_count"):
            request.datagrid_count = 0

        self.id = "datagrid-%s" % request.datagrid_count
        request.datagrid_count += 1

        # Customizable variables
        self.title = title
        self.profile_sort_field = None
        self.profile_columns_field = None
        self.paginate_by = 50
        self.paginate_orphans = 3
        self.listview_template = 'datagrid/listview.html'
        self.column_header_template = 'datagrid/column_header.html'
        self.cell_template = 'datagrid/cell.html'

        for attr in dir(self):
            column = getattr(self, attr)
            if isinstance(column, Column):
                self.all_columns.append(column)
                column.datagrid = self
                column.id = attr

                # Reset the column.
                column.reset()

                if not column.field_name:
                    column.field_name = column.id

                if not column.db_field:
                    column.db_field = column.field_name

                self.db_field_map[column.id] = column.db_field

        self.all_columns.sort(key=lambda x: x.label)

    def load_state(self, render_context=None):
        """
        Loads the state of the datagrid.

        This will retrieve the user-specified or previously stored
        sorting order and columns list, as well as any state a subclass
        may need.
        """
        if self.state_loaded:
            return

        profile_sort_list = None
        profile_columns_list = None
        profile = None
        profile_dirty = False

        # Get the saved settings for this grid in the profile. These will
        # work as defaults and allow us to determine if we need to save
        # the profile.
        if self.request.user.is_authenticated():
            try:
                profile = self.request.user.get_profile()

                if self.profile_sort_field:
                    profile_sort_list = \
                        getattr(profile, self.profile_sort_field, None)

                if self.profile_columns_field:
                    profile_columns_list = \
                        getattr(profile, self.profile_columns_field, None)
            except SiteProfileNotAvailable:
                pass
            except ObjectDoesNotExist:
                pass


        # Figure out the columns we're going to display
        # We're also going to calculate the column widths based on the
        # shrink and expand values.
        colnames_str = self.request.GET.get('columns', profile_columns_list)

        if colnames_str:
            colnames = colnames_str.split(',')
        else:
            colnames = self.default_columns
            colnames_str = ",".join(colnames)

        expand_columns = []
        normal_columns = []

        for colname in colnames:
            try:
                column = getattr(self, colname)
            except AttributeError:
                # The user specified a column that doesn't exist. Skip it.
                continue

            self.columns.append(column)
            column.active = True

            if column.expand:
                # This column is requesting all remaining space. Save it for
                # later so we can tell how much to give it. Each expanded
                # column will count as two normal columns when calculating
                # the normal sized columns.
                expand_columns.append(column)
            elif column.shrink:
                # Make this as small as possible.
                column.width = 0
            else:
                # We'll divide the column widths equally after we've built
                # up the lists of expanded and normal sized columns.
                normal_columns.append(column)

        self.columns[-1].last = True

        # Try to figure out the column widths for each column.
        # We'll start with the normal sized columns.
        total_pct = 100

        # Each expanded column counts as two normal columns.
        normal_column_width = total_pct / (len(self.columns) +
                                           len(expand_columns))

        for column in normal_columns:
            column.width = normal_column_width
            total_pct -= normal_column_width

        if len(expand_columns) > 0:
            expanded_column_width = total_pct / len(expand_columns)
        else:
            expanded_column_width = 0

        for column in expand_columns:
            column.width = expanded_column_width


        # Now get the sorting order for the columns.
        sort_str = self.request.GET.get('sort', profile_sort_list)

        if sort_str:
            self.sort_list = sort_str.split(',')
        else:
            self.sort_list = self.default_sort
            sort_str = ",".join(self.sort_list)


        # A subclass might have some work to do for loading and saving
        # as well.
        if self.load_extra_state(profile):
            profile_dirty = True


        # Now that we have all that, figure out if we need to save new
        # settings back to the profile.
        if profile:
            if self.profile_columns_field and \
               colnames_str != profile_columns_list:
                setattr(profile, self.profile_columns_field, colnames_str)
                profile_dirty = True

            if self.profile_sort_field and sort_str != profile_sort_list:
                setattr(profile, self.profile_sort_field, sort_str)
                profile_dirty = True

            if profile_dirty:
                profile.save()

        self.state_loaded = True

        # Fetch the list of objects and have it ready.
        self.precompute_objects(render_context)

    def load_extra_state(self, profile):
        """
        Loads any extra state needed for this grid.

        This is used by subclasses that may have additional data to load
        and save. This should return True if any profile-stored state has
        changed, or False otherwise.
        """
        return False

    def precompute_objects(self, render_context=None):
        """
        Builds the queryset and stores the list of objects for use in
        rendering the datagrid.
        """
        query = self.queryset
        use_select_related = False

        # Generate the actual list of fields we'll be sorting by
        sort_list = []
        for sort_item in self.sort_list:
            if sort_item[0] == "-":
                base_sort_item = sort_item[1:]
                prefix = "-"
            else:
                base_sort_item = sort_item
                prefix = ""

            if sort_item and base_sort_item in self.db_field_map:
                db_field = self.db_field_map[base_sort_item]
                sort_list.append(prefix + db_field)

                # Lookups spanning tables require that we query from those
                # tables. In order to keep things simple, we'll just use
                # select_related so that we don't have to figure out the
                # table relationships. We only do this if we have a lookup
                # spanning tables.
                if '.' in db_field:
                    use_select_related = True

        if sort_list:
            query = query.order_by(*sort_list)

        self.paginator = QuerySetPaginator(query.distinct(), self.paginate_by,
                                           self.paginate_orphans)

        page_num = self.request.GET.get('page', 1)

        # Accept either "last" or a valid page number.
        if page_num == "last":
            page_num = self.paginator.num_pages

        try:
            self.page = self.paginator.page(page_num)
        except InvalidPage:
            raise Http404

        self.id_list = []

        if self.optimize_sorts and len(sort_list) > 0:
            # This can be slow when sorting by multiple columns. If we
            # have multiple items in the sort list, we'll request just the
            # IDs and then fetch the actual details from that.
            self.id_list = list(self.page.object_list.values_list(
                'pk', flat=True))

            # Make sure to unset the order. We can't meaningfully order these
            # results in the query, as what we really want is to keep it in
            # the order specified in id_list, and we certainly don't want
            # the database to do any special ordering (possibly slowing things
            # down). We'll set the order properly in a minute.
            self.page.object_list = self.post_process_queryset(
                self.queryset.model.objects.filter(
                    pk__in=self.id_list).order_by())

        if use_select_related:
            self.page.object_list = \
                self.page.object_list.select_related(depth=1)

        if self.id_list:
            # The database will give us the items in a more or less random
            # order, since it doesn't know to keep it in the order provided by
            # the ID list. This will place the results back in the order we
            # expect.
            index = dict([(id, pos) for (pos, id) in enumerate(self.id_list)])
            object_list = [None] * len(self.id_list)

            for obj in list(self.page.object_list):
                object_list[index[obj.pk]] = obj
        else:
            # Grab the whole list at once. We know it won't be too large,
            # and it will prevent one query per row.
            object_list = list(self.page.object_list)

        for column in self.columns:
            column.collect_objects(object_list)

        if render_context is None:
            render_context = self._build_render_context()

        self.rows = [
            {
                'object': obj,
                'cells': [column.render_cell(obj, render_context)
                          for column in self.columns]
            }
            for obj in object_list if obj is not None
        ]

    def post_process_queryset(self, queryset):
        """
        Processes a QuerySet after the initial query has been built and
        pagination applied. This is only used when optimizing a sort.

        By default, this just returns the existing queryset. Custom datagrid
        subclasses can override this to add additional queries (such as
        subqueries in an extra() call) for use in the cell renderers.

        When optimize_sorts is True, subqueries (using extra()) on the initial
        QuerySet passed to the datagrid will be stripped from the final
        result. This function can be used to re-add those subqueries.
        """
        for column in self.columns:
            queryset = column.augment_queryset(queryset)

        return queryset

    def render_listview(self, render_context=None):
        """
        Renders the standard list view of the grid.

        This can be called from templates.
        """
        try:
            if render_context is None:
                render_context = self._build_render_context()

            self.load_state(render_context)

            context = Context({
                'datagrid': self,
                'is_paginated': self.page.has_other_pages(),
                'results_per_page': self.paginate_by,
                'has_next': self.page.has_next(),
                'has_previous': self.page.has_previous(),
                'page': self.page.number,
                'next': self.page.next_page_number(),
                'previous': self.page.previous_page_number(),
                'last_on_page': self.page.end_index(),
                'first_on_page': self.page.start_index(),
                'pages': self.paginator.num_pages,
                'hits': self.paginator.count,
                'page_range': self.paginator.page_range,
            })
            context.update(self.extra_context)
            context.update(render_context)

            return mark_safe(render_to_string(self.listview_template,
                                              context))
        except Exception:
            trace = traceback.format_exc();
            logging.error('Failed to render datagrid:\n%s' % trace,
                          extra={
                              'request': self.request,
                          })
            return mark_safe('<pre>%s</pre>' % trace)

    def render_listview_to_response(self, request=None, render_context=None):
        """
        Renders the listview to a response, preventing caching in the
        process.
        """
        response = HttpResponse(unicode(self.render_listview(render_context)))
        patch_cache_control(response, no_cache=True, no_store=True, max_age=0,
                            must_revalidate=True)
        return response

    def render_to_response(self, template_name, extra_context={}):
        """
        Renders a template containing this datagrid as a context variable.
        """
        render_context = self._build_render_context()
        self.load_state(render_context)

        # If the caller is requesting just this particular grid, return it.
        if self.request.GET.get('gridonly', False) and \
           self.request.GET.get('datagrid-id', None) == self.id:
            return self.render_listview_to_response(
                render_context=render_context)

        context = Context({
            'datagrid': self
        })
        context.update(extra_context)
        context.update(render_context)

        return render_to_response(template_name, context)

    def _build_render_context(self):
        """Builds a dictionary containing RequestContext contents.

        A RequestContext can be expensive, so it's best to reuse the
        contents of one when possible. This is not easy with a standard
        RequestContext, but it's possible to build one and then pull out
        the contents into a dictionary.
        """
        request_context = RequestContext(self.request)
        render_context = {}

        for d in request_context:
            render_context.update(d)

        return render_context

    @staticmethod
    def link_to_object(obj, value):
        return obj.get_absolute_url()

    @staticmethod
    def link_to_value(obj, value):
        return value.get_absolute_url()
Exemple #34
0
def multi_object_list(request, querysets, template_name=None, template_loader=loader,
        extra_context=None, context_processors=None, mimetype=None):
    """
    Generic list of objects.  Querysets is a sequence of tuples, where the first element
    of each tuple is the queryset, and the second element is a dictionary specifying the
    parameters for that queryset.  Unused parameters are set to the following default values.
       Defaults:
         name: required, throws error if not present
         paginate_by: don't paginate
         page: get page from HTTP request
         allow_emptys: None


    Templates: ``<app_label>/<model_name>_[<model_name>_]*list.html``
    Context:
        object_contexts
          dict mapping queryname to a dict containing the following keys:
            queryname_list
                list of objects
            is_paginated
                are the results paginated?
            results_per_page
                number of objects per page (if paginated)
            has_next
                is there a next page?
            has_previous
                is there a prev page?
            page
                the current page
            next
                the next page
            previous
                the previous page
            pages
                number of pages, total
            hits
                number of objects, total
            last_on_page
                the result number of the last of object in the
                object_list (1-indexed)
            first_on_page
                the result number of the first object in the
                object_list (1-indexed)
            page_range:
                A list of the page numbers (1-indexed).
    """
    if extra_context is None: extra_context = {}
    object_contexts = {}
    for querytuple in querysets:
        queryset, parameters = querytuple
        queryset = queryset._clone()
        paginate_by = parameters.get('paginate_by', None)
        allow_empty = parameters.get('allow_empty', None)
        if paginate_by:
            paginator = QuerySetPaginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
            page = parameters.get('page', None)
            if not page:
                page = request.GET.get((parameters['name'] + '_page'), 1)
            try:
                page_number = int(page)
            except ValueError:
                if page == 'last':
                    page_number = paginator.num_pages
                else:
                    # Page is not 'last', nor can it be converted to an int.
                    raise Http404
            try:
                page_obj = paginator.page(page_number)
            except InvalidPage:
                raise Http404
            object_context = {
                '%s_list' % parameters['name']: page_obj.object_list,
                'paginator': paginator,
                'page_obj': page_obj,

                # Legacy template context stuff. New templates should use page_obj
                # to access this instead.
                'is_paginated': page_obj.has_other_pages(),
                'results_per_page': paginator.per_page,
                'has_next': page_obj.has_next(),
                'has_previous': page_obj.has_previous(),
                'page': page_obj.number,
                'next': page_obj.next_page_number(),
                'previous': page_obj.previous_page_number(),
                'first_on_page': page_obj.start_index(),
                'last_on_page': page_obj.end_index(),
                'pages': paginator.num_pages,
                'hits': paginator.count,
                'page_range': paginator.page_range,
            }
        else:
            object_context = {
                '%s_list' % parameters['name']: queryset,
                'paginator': None,
                'page_obj': None,
                'is_paginated': False,
            }
            if not allow_empty and len(queryset) == 0:
                raise Http404
        object_contexts[parameters['name']] = object_context
    c = RequestContext(request,
        object_contexts
        , context_processors)
    for key, value in extra_context.items():
        if callable(value):
            c[key] = value()
        else:
            c[key] = value
    if not template_name:
        object_names = []
        app_label = ''
        for (queryset, parameters) in querysets:
            model = queryset.model
            app_label = model._meta.app_label
            object_names.append(model._meta.object_name.lower())
        object_name = '_'.join(object_names)
        template_name = "%s/%slist.html" % (app_label, object_name)
    t = template_loader.get_template(template_name)
    return HttpResponse(t.render(c), mimetype=mimetype)
Exemple #35
0
class DataGrid(object):
    """
    A representation of a list of objects, sorted and organized by
    columns. The sort order and column lists can be customized. allowing
    users to view this data however they prefer.

    This is meant to be subclassed for specific uses. The subclasses are
    responsible for defining one or more column types. It can also set
    one or more of the following optional variables:

        * 'title':                  The title of the grid.
        * 'profile_sort_field':     The variable name in the user profile
                                    where the sort order can be loaded and
                                    saved.
        * 'profile_columns_field":  The variable name in the user profile
                                    where the columns list can be loaded and
                                    saved.
        * 'paginate_by':            The number of items to show on each page
                                    of the grid. The default is 50.
        * 'paginate_orphans':       If this number of objects or fewer are
                                    on the last page, it will be rolled into
                                    the previous page. The default is 3.
        * 'page':                   The page to display. If this is not
                                    specified, the 'page' variable passed
                                    in the URL will be used, or 1 if that is
                                    not specified.
        * 'listview_template':      The template used to render the list view.
                                    The default is 'datagrid/listview.html'
        * 'column_header_template': The template used to render each column
                                    header. The default is
                                    'datagrid/column_header.html'
        * 'cell_template':          The template used to render a cell of
                                    data. The default is 'datagrid/cell.html'
    """
    def __init__(self, request, queryset=None, title="", extra_context={}):
        self.request = request
        self.queryset = queryset
        self.rows = []
        self.columns = []
        self.all_columns = []
        self.db_field_map = {}
        self.paginator = None
        self.page = None
        self.sort_list = None
        self.state_loaded = False
        self.page_num = 0
        self.id = None
        self.extra_context = dict(extra_context)

        if not hasattr(request, "datagrid_count"):
            request.datagrid_count = 0

        self.id = "datagrid-%s" % request.datagrid_count
        request.datagrid_count += 1

        # Customizable variables
        self.title = title
        self.profile_sort_field = None
        self.profile_columns_field = None
        self.paginate_by = 50
        self.paginate_orphans = 3
        self.listview_template = 'datagrid/listview.html'
        self.column_header_template = 'datagrid/column_header.html'
        self.cell_template = 'datagrid/cell.html'

        for attr in dir(self):
            column = getattr(self, attr)
            if isinstance(column, Column):
                self.all_columns.append(column)
                column.datagrid = self
                column.id = attr

                # Reset the column.
                column.active = False
                column.last = False
                column.width = 0

                if not column.field_name:
                    column.field_name = column.id

                if not column.db_field:
                    column.db_field = column.field_name

                self.db_field_map[column.id] = column.db_field

        self.all_columns.sort(key=lambda x: x.label)


    def load_state(self):
        """
        Loads the state of the datagrid.

        This will retrieve the user-specified or previously stored
        sorting order and columns list, as well as any state a subclass
        may need.
        """
        if self.state_loaded:
            return

        profile_sort_list = None
        profile_columns_list = None
        profile = None
        profile_dirty = False

        # Get the saved settings for this grid in the profile. These will
        # work as defaults and allow us to determine if we need to save
        # the profile.
        if self.request.user.is_authenticated():
            try:
                profile = self.request.user.get_profile()

                if self.profile_sort_field:
                    profile_sort_list = \
                        getattr(profile, self.profile_sort_field, None)

                if self.profile_columns_field:
                    profile_columns_list = \
                        getattr(profile, self.profile_columns_field, None)
            except SiteProfileNotAvailable:
                pass
            except ObjectDoesNotExist:
                pass


        # Figure out the columns we're going to display
        # We're also going to calculate the column widths based on the
        # shrink and expand values.
        colnames_str = self.request.GET.get('columns', profile_columns_list)

        if colnames_str:
            colnames = colnames_str.split(',')
        else:
            colnames = self.default_columns
            colnames_str = ",".join(colnames)

        expand_columns = []
        normal_columns = []

        for colname in colnames:
            try:
                column = getattr(self, colname)
            except AttributeError:
                # The user specified a column that doesn't exist. Skip it.
                continue

            self.columns.append(column)
            column.active = True

            if column.expand:
                # This column is requesting all remaining space. Save it for
                # later so we can tell how much to give it. Each expanded
                # column will count as two normal columns when calculating
                # the normal sized columns.
                expand_columns.append(column)
            elif column.shrink:
                # Make this as small as possible.
                column.width = 0
            else:
                # We'll divide the column widths equally after we've built
                # up the lists of expanded and normal sized columns.
                normal_columns.append(column)

        self.columns[-1].last = True

        # Try to figure out the column widths for each column.
        # We'll start with the normal sized columns.
        total_pct = 100

        # Each expanded column counts as two normal columns.
        normal_column_width = total_pct / (len(self.columns) +
                                           len(expand_columns))

        for column in normal_columns:
            column.width = normal_column_width
            total_pct -= normal_column_width

        if len(expand_columns) > 0:
            expanded_column_width = total_pct / len(expand_columns)
        else:
            expanded_column_width = 0

        for column in expand_columns:
            column.width = expanded_column_width


        # Now get the sorting order for the columns.
        sort_str = self.request.GET.get('sort', profile_sort_list)

        if sort_str:
            self.sort_list = sort_str.split(',')
        else:
            self.sort_list = self.default_sort
            sort_str = ",".join(self.sort_list)


        # A subclass might have some work to do for loading and saving
        # as well.
        if self.load_extra_state(profile):
            profile_dirty = True


        # Now that we have all that, figure out if we need to save new
        # settings back to the profile.
        if profile:
            if self.profile_columns_field and \
               colnames_str != profile_columns_list:
                setattr(profile, self.profile_columns_field, colnames_str)
                profile_dirty = True

            if self.profile_sort_field and sort_str != profile_sort_list:
                setattr(profile, self.profile_sort_field, sort_str)
                profile_dirty = True

            if profile_dirty:
                profile.save()

        self.state_loaded = True

        # Fetch the list of objects and have it ready.
        self.precompute_objects()


    def load_extra_state(self, profile):
        """
        Loads any extra state needed for this grid.

        This is used by subclasses that may have additional data to load
        and save. This should return True if any profile-stored state has
        changed, or False otherwise.
        """
        return False

    def precompute_objects(self):
        """
        Builds the queryset and stores the list of objects for use in
        rendering the datagrid.
        """
        query = self.queryset

        # Generate the actual list of fields we'll be sorting by
        sort_list = []
        for sort_item in self.sort_list:
            if sort_item[0] == "-":
                base_sort_item = sort_item[1:]
                prefix = "-"
            else:
                base_sort_item = sort_item
                prefix = ""

            if sort_item and base_sort_item in self.db_field_map:
                db_field = self.db_field_map[base_sort_item]
                sort_list.append(prefix + db_field)

        if sort_list:
            query = query.order_by(*sort_list)

        # Get some of the objects we're likely to look up, in order to
        # reduce future queries.
        query = query.select_related(depth=1)

        self.paginator = QuerySetPaginator(query, self.paginate_by,
                                           self.paginate_orphans)

        page_num = self.request.GET.get('page', 1)

        # Accept either "last" or a valid page number.
        if page_num == "last":
            page_num = self.paginator.num_pages

        try:
            self.page = self.paginator.page(page_num)
        except InvalidPage:
            raise Http404

        self.rows = []

        for obj in self.page.object_list:
            self.rows.append({
                'object': obj,
                'cells': [column.render_cell(obj) for column in self.columns]
            })

    def render_listview(self):
        """
        Renders the standard list view of the grid.

        This can be called from templates.
        """
        self.load_state()

        context = {
            'datagrid': self,
            'is_paginated': self.page.has_other_pages(),
            'results_per_page': self.paginate_by,
            'has_next': self.page.has_next(),
            'has_previous': self.page.has_previous(),
            'page': self.page.number,
            'next': self.page.next_page_number(),
            'previous': self.page.previous_page_number(),
            'last_on_page': self.page.end_index(),
            'first_on_page': self.page.start_index(),
            'pages': self.paginator.num_pages,
            'hits': self.paginator.count,
            'page_range': self.paginator.page_range,
        }
        context.update(self.extra_context)

        return mark_safe(render_to_string(self.listview_template,
            RequestContext(self.request, context)))

    @cache_control(no_cache=True, no_store=True, max_age=0,
                   must_revalidate=True)
    def render_listview_to_response(self):
        """
        Renders the listview to a response, preventing caching in the
        process.
        """
        return HttpResponse(unicode(self.render_listview()))

    def render_to_response(self, template_name, extra_context={}):
        """
        Renders a template containing this datagrid as a context variable.
        """
        self.load_state()

        # If the caller is requesting just this particular grid, return it.
        if self.request.GET.get('gridonly', False) and \
           self.request.GET.get('datagrid-id', None) == self.id:
            return self.render_listview_to_response()

        context = {
            'datagrid': self
        }
        context.update(extra_context)
        context.update(self.extra_context)

        return render_to_response(template_name, RequestContext(self.request,
                                                                context))

    @staticmethod
    def link_to_object(obj, value):
        return obj.get_absolute_url()

    @staticmethod
    def link_to_value(obj, value):
        return value.get_absolute_url()
Exemple #36
0
class DataGrid(object):
    """
    A representation of a list of objects, sorted and organized by
    columns. The sort order and column lists can be customized. allowing
    users to view this data however they prefer.

    This is meant to be subclassed for specific uses. The subclasses are
    responsible for defining one or more column types. It can also set
    one or more of the following optional variables:

        * 'title':                  The title of the grid.
        * 'profile_sort_field':     The variable name in the user profile
                                    where the sort order can be loaded and
                                    saved.
        * 'profile_columns_field":  The variable name in the user profile
                                    where the columns list can be loaded and
                                    saved.
        * 'paginate_by':            The number of items to show on each page
                                    of the grid. The default is 50.
        * 'paginate_orphans':       If this number of objects or fewer are
                                    on the last page, it will be rolled into
                                    the previous page. The default is 3.
        * 'page':                   The page to display. If this is not
                                    specified, the 'page' variable passed
                                    in the URL will be used, or 1 if that is
                                    not specified.
        * 'listview_template':      The template used to render the list view.
                                    The default is 'datagrid/listview.html'
        * 'column_header_template': The template used to render each column
                                    header. The default is
                                    'datagrid/column_header.html'
        * 'cell_template':          The template used to render a cell of
                                    data. The default is 'datagrid/cell.html'
        * 'optimize_sorts':         Whether or not to optimize queries when
                                    using multiple sorts. This can offer a
                                    speed improvement, but may need to be
                                    turned off for more advanced querysets
                                    (such as when using extra()).
                                    The default is True.
    """
    def __init__(self,
                 request,
                 queryset=None,
                 title="",
                 extra_context={},
                 optimize_sorts=True):
        self.request = request
        self.queryset = queryset
        self.rows = []
        self.columns = []
        self.all_columns = []
        self.db_field_map = {}
        self.id_list = []
        self.paginator = None
        self.page = None
        self.sort_list = None
        self.state_loaded = False
        self.page_num = 0
        self.id = None
        self.extra_context = dict(extra_context)
        self.optimize_sorts = optimize_sorts
        self.cell_template_obj = None
        self.column_header_template_obj = None

        if not hasattr(request, "datagrid_count"):
            request.datagrid_count = 0

        self.id = "datagrid-%s" % request.datagrid_count
        request.datagrid_count += 1

        # Customizable variables
        self.title = title
        self.profile_sort_field = None
        self.profile_columns_field = None
        self.paginate_by = 50
        self.paginate_orphans = 3
        self.listview_template = 'datagrid/listview.html'
        self.column_header_template = 'datagrid/column_header.html'
        self.cell_template = 'datagrid/cell.html'

        for attr in dir(self):
            column = getattr(self, attr)
            if isinstance(column, Column):
                self.all_columns.append(column)
                column.datagrid = self
                column.id = attr

                # Reset the column.
                column.reset()

                if not column.field_name:
                    column.field_name = column.id

                if not column.db_field:
                    column.db_field = column.field_name

                self.db_field_map[column.id] = column.db_field

        self.all_columns.sort(key=lambda x: x.label)

    def load_state(self):
        """
        Loads the state of the datagrid.

        This will retrieve the user-specified or previously stored
        sorting order and columns list, as well as any state a subclass
        may need.
        """
        if self.state_loaded:
            return

        profile_sort_list = None
        profile_columns_list = None
        profile = None
        profile_dirty = False

        # Get the saved settings for this grid in the profile. These will
        # work as defaults and allow us to determine if we need to save
        # the profile.
        if self.request.user.is_authenticated():
            try:
                profile = self.request.user.get_profile()

                if self.profile_sort_field:
                    profile_sort_list = \
                        getattr(profile, self.profile_sort_field, None)

                if self.profile_columns_field:
                    profile_columns_list = \
                        getattr(profile, self.profile_columns_field, None)
            except SiteProfileNotAvailable:
                pass
            except ObjectDoesNotExist:
                pass

        # Figure out the columns we're going to display
        # We're also going to calculate the column widths based on the
        # shrink and expand values.
        colnames_str = self.request.GET.get('columns', profile_columns_list)

        if colnames_str:
            colnames = colnames_str.split(',')
        else:
            colnames = self.default_columns
            colnames_str = ",".join(colnames)

        expand_columns = []
        normal_columns = []

        for colname in colnames:
            try:
                column = getattr(self, colname)
            except AttributeError:
                # The user specified a column that doesn't exist. Skip it.
                continue

            self.columns.append(column)
            column.active = True

            if column.expand:
                # This column is requesting all remaining space. Save it for
                # later so we can tell how much to give it. Each expanded
                # column will count as two normal columns when calculating
                # the normal sized columns.
                expand_columns.append(column)
            elif column.shrink:
                # Make this as small as possible.
                column.width = 0
            else:
                # We'll divide the column widths equally after we've built
                # up the lists of expanded and normal sized columns.
                normal_columns.append(column)

        self.columns[-1].last = True

        # Try to figure out the column widths for each column.
        # We'll start with the normal sized columns.
        total_pct = 100

        # Each expanded column counts as two normal columns.
        normal_column_width = total_pct / (len(self.columns) +
                                           len(expand_columns))

        for column in normal_columns:
            column.width = normal_column_width
            total_pct -= normal_column_width

        if len(expand_columns) > 0:
            expanded_column_width = total_pct / len(expand_columns)
        else:
            expanded_column_width = 0

        for column in expand_columns:
            column.width = expanded_column_width

        # Now get the sorting order for the columns.
        sort_str = self.request.GET.get('sort', profile_sort_list)

        if sort_str:
            self.sort_list = sort_str.split(',')
        else:
            self.sort_list = self.default_sort
            sort_str = ",".join(self.sort_list)

        # A subclass might have some work to do for loading and saving
        # as well.
        if self.load_extra_state(profile):
            profile_dirty = True

        # Now that we have all that, figure out if we need to save new
        # settings back to the profile.
        if profile:
            if self.profile_columns_field and \
               colnames_str != profile_columns_list:
                setattr(profile, self.profile_columns_field, colnames_str)
                profile_dirty = True

            if self.profile_sort_field and sort_str != profile_sort_list:
                setattr(profile, self.profile_sort_field, sort_str)
                profile_dirty = True

            if profile_dirty:
                profile.save()

        self.state_loaded = True

        # Fetch the list of objects and have it ready.
        self.precompute_objects()

    def load_extra_state(self, profile):
        """
        Loads any extra state needed for this grid.

        This is used by subclasses that may have additional data to load
        and save. This should return True if any profile-stored state has
        changed, or False otherwise.
        """
        return False

    def precompute_objects(self):
        """
        Builds the queryset and stores the list of objects for use in
        rendering the datagrid.
        """
        query = self.queryset
        use_select_related = False

        # Generate the actual list of fields we'll be sorting by
        sort_list = []
        for sort_item in self.sort_list:
            if sort_item[0] == "-":
                base_sort_item = sort_item[1:]
                prefix = "-"
            else:
                base_sort_item = sort_item
                prefix = ""

            if sort_item and base_sort_item in self.db_field_map:
                db_field = self.db_field_map[base_sort_item]
                sort_list.append(prefix + db_field)

                # Lookups spanning tables require that we query from those
                # tables. In order to keep things simple, we'll just use
                # select_related so that we don't have to figure out the
                # table relationships. We only do this if we have a lookup
                # spanning tables.
                if '.' in db_field:
                    use_select_related = True

        if sort_list:
            query = query.order_by(*sort_list)

        self.paginator = QuerySetPaginator(query.distinct(), self.paginate_by,
                                           self.paginate_orphans)

        page_num = self.request.GET.get('page', 1)

        # Accept either "last" or a valid page number.
        if page_num == "last":
            page_num = self.paginator.num_pages

        try:
            self.page = self.paginator.page(page_num)
        except InvalidPage:
            raise Http404

        self.id_list = []

        if self.optimize_sorts and len(sort_list) > 0:
            # This can be slow when sorting by multiple columns. If we
            # have multiple items in the sort list, we'll request just the
            # IDs and then fetch the actual details from that.
            self.id_list = list(
                self.page.object_list.values_list('pk', flat=True))

            # Make sure to unset the order. We can't meaningfully order these
            # results in the query, as what we really want is to keep it in
            # the order specified in id_list, and we certainly don't want
            # the database to do any special ordering (possibly slowing things
            # down). We'll set the order properly in a minute.
            self.page.object_list = self.post_process_queryset(
                self.queryset.model.objects.filter(
                    pk__in=self.id_list).order_by())

        if use_select_related:
            self.page.object_list = \
                self.page.object_list.select_related(depth=1)

        if self.id_list:
            # The database will give us the items in a more or less random
            # order, since it doesn't know to keep it in the order provided by
            # the ID list. This will place the results back in the order we
            # expect.
            index = dict([(id, pos) for (pos, id) in enumerate(self.id_list)])
            object_list = [None] * len(self.id_list)

            for obj in list(self.page.object_list):
                object_list[index[obj.pk]] = obj
        else:
            # Grab the whole list at once. We know it won't be too large,
            # and it will prevent one query per row.
            object_list = list(self.page.object_list)

        for column in self.columns:
            column.collect_objects(object_list)

        self.rows = [{
            'object':
            obj,
            'cells': [column.render_cell(obj) for column in self.columns]
        } for obj in object_list if obj is not None]

    def post_process_queryset(self, queryset):
        """
        Processes a QuerySet after the initial query has been built and
        pagination applied. This is only used when optimizing a sort.

        By default, this just returns the existing queryset. Custom datagrid
        subclasses can override this to add additional queries (such as
        subqueries in an extra() call) for use in the cell renderers.

        When optimize_sorts is True, subqueries (using extra()) on the initial
        QuerySet passed to the datagrid will be stripped from the final
        result. This function can be used to re-add those subqueries.
        """
        for column in self.columns:
            queryset = column.augment_queryset(queryset)

        return queryset

    def render_listview(self):
        """
        Renders the standard list view of the grid.

        This can be called from templates.
        """
        self.load_state()

        context = {
            'datagrid': self,
            'is_paginated': self.page.has_other_pages(),
            'results_per_page': self.paginate_by,
            'has_next': self.page.has_next(),
            'has_previous': self.page.has_previous(),
            'page': self.page.number,
            'next': self.page.next_page_number(),
            'previous': self.page.previous_page_number(),
            'last_on_page': self.page.end_index(),
            'first_on_page': self.page.start_index(),
            'pages': self.paginator.num_pages,
            'hits': self.paginator.count,
            'page_range': self.paginator.page_range,
        }
        context.update(self.extra_context)

        return mark_safe(
            render_to_string(self.listview_template,
                             RequestContext(self.request, context)))

    def render_listview_to_response(self, request=None):
        """
        Renders the listview to a response, preventing caching in the
        process.
        """
        response = HttpResponse(unicode(self.render_listview()))
        patch_cache_control(response,
                            no_cache=True,
                            no_store=True,
                            max_age=0,
                            must_revalidate=True)
        return response

    def render_to_response(self, template_name, extra_context={}):
        """
        Renders a template containing this datagrid as a context variable.
        """
        self.load_state()

        # If the caller is requesting just this particular grid, return it.
        if self.request.GET.get('gridonly', False) and \
           self.request.GET.get('datagrid-id', None) == self.id:
            return self.render_listview_to_response()

        context = {'datagrid': self}
        context.update(extra_context)
        context.update(self.extra_context)

        return render_to_response(template_name,
                                  RequestContext(self.request, context))

    @staticmethod
    def link_to_object(obj, value):
        return obj.get_absolute_url()

    @staticmethod
    def link_to_value(obj, value):
        return value.get_absolute_url()
Exemple #37
0
class RelatedDataGrid(DataGrid):
    """Version of djblets DataGrid modified for BPG needs

  This class is almost identical to it's superclass, DataGrid. The only
differences are that the precomputer_objects and render_listview methods have
been overridden. However, the code in both methods is very similar to the
DataGrid superclass code for these methods. Small, customized 'tweaks' have
been made to the original and are documented per method.
  """
    def precompute_objects(self):
        """
      Builds the queryset and stores the list of objects for use in
      rendering the datagrid.

      Just like the method in the superclass, 
      except we use self.depth as the depth for select_related
      """
        query = self.queryset
        use_select_related = False

        # Generate the actual list of fields we'll be sorting by
        sort_list = []
        for sort_item in self.sort_list:
            if sort_item[0] == "-":
                base_sort_item = sort_item[1:]
                prefix = "-"
            else:
                base_sort_item = sort_item
                prefix = ""

            if sort_item and base_sort_item in self.db_field_map:
                db_field = self.db_field_map[base_sort_item]
                sort_list.append(prefix + db_field)

                # Lookups spanning tables require that we query from those
                # tables. In order to keep things simple, we'll just use
                # select_related so that we don't have to figure out the
                # table relationships. We only do this if we have a lookup
                # spanning tables.
                if '.' in db_field:
                    use_select_related = True

        if sort_list:
            query = query.order_by(*sort_list)

        if use_select_related:
            query = query.select_related(depth=self.depth)

        self.paginator = QuerySetPaginator(query, self.paginate_by,
                                           self.paginate_orphans)

        page_num = self.request.GET.get('page', 1)

        # Accept either "last" or a valid page number.
        if page_num == "last":
            page_num = self.paginator.num_pages

        try:
            self.page = self.paginator.page(page_num)
        except InvalidPage:
            raise Http404

        self.rows = []

        for obj in self.page.object_list:
            self.rows.append({
                'object':
                obj,
                'cells': [column.render_cell(obj) for column in self.columns]
            })

    def render_listview(self):
        """
      Renders the standard list view of the grid.

      This can be called from templates.

      Just like the method in the superclass, except it updates the 
      context with self.extra_context first.
      """
        self.load_state()

        context = RequestContext(
            self.request, {
                'datagrid': self,
                'is_paginated': self.page.has_other_pages(),
                'results_per_page': self.paginate_by,
                'has_next': self.page.has_next(),
                'has_previous': self.page.has_previous(),
                'page': self.page.number,
                'next': self.page.next_page_number(),
                'previous': self.page.previous_page_number(),
                'last_on_page': self.page.end_index(),
                'first_on_page': self.page.start_index(),
                'pages': self.paginator.num_pages,
                'hits': self.paginator.count,
                'page_range': self.paginator.page_range,
            })
        context.update(self.extra_context)
        return mark_safe(render_to_string(self.listview_template, context))
def object_list(
    request,
    queryset,
    paginate_by=None,
    page=None,
    allow_empty=True,
    template_name=None,
    template_loader=loader,
    extra_context=None,
    context_processors=None,
    template_object_name="object",
    mimetype=None,
):
    """
    Generic list of objects.

    Templates: ``<app_label>/<model_name>_list.html``
    Context:
        object_list
            list of objects
        is_paginated
            are the results paginated?
        results_per_page
            number of objects per page (if paginated)
        has_next
            is there a next page?
        has_previous
            is there a prev page?
        page
            the current page
        next
            the next page
        previous
            the previous page
        pages
            number of pages, total
        hits
            number of objects, total
        last_on_page
            the result number of the last of object in the
            object_list (1-indexed)
        first_on_page
            the result number of the first object in the
            object_list (1-indexed)
        page_range:
            A list of the page numbers (1-indexed).
    """
    if extra_context is None:
        extra_context = {}
    queryset = queryset._clone()
    if paginate_by:
        paginator = QuerySetPaginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
        if not page:
            page = request.GET.get("page", 1)
        try:
            page_number = int(page)
        except ValueError:
            if page == "last":
                page_number = paginator.num_pages
            else:
                # Page is not 'last', nor can it be converted to an int.
                raise Http404
        try:
            page_obj = paginator.page(page_number)
        except InvalidPage:
            raise Http404
        c = RequestContext(
            request,
            {
                "%s_list" % template_object_name: page_obj.object_list,
                "paginator": paginator,
                "page_obj": page_obj,
                # Legacy template context stuff. New templates should use page_obj
                # to access this instead.
                "is_paginated": page_obj.has_other_pages(),
                "results_per_page": paginator.per_page,
                "has_next": page_obj.has_next(),
                "has_previous": page_obj.has_previous(),
                "page": page_obj.number,
                "next": page_obj.next_page_number(),
                "previous": page_obj.previous_page_number(),
                "first_on_page": page_obj.start_index(),
                "last_on_page": page_obj.end_index(),
                "pages": paginator.num_pages,
                "hits": paginator.count,
                "page_range": paginator.page_range,
            },
            context_processors,
        )
    else:
        c = RequestContext(
            request,
            {"%s_list" % template_object_name: queryset, "paginator": None, "page_obj": None, "is_paginated": False},
            context_processors,
        )
        if not allow_empty and len(queryset) == 0:
            raise Http404
    for key, value in extra_context.items():
        if callable(value):
            c[key] = value()
        else:
            c[key] = value
    if not template_name:
        model = queryset.model
        template_name = "%s/%s_list.html" % (model._meta.app_label, model._meta.object_name.lower())
    t = template_loader.get_template(template_name)
    return HttpResponse(t.render(c), mimetype=mimetype)
Exemple #39
0
def object_list(request,
                queryset,
                paginate_by=None,
                page=None,
                allow_empty=True,
                template_name=None,
                template_loader=loader,
                extra_context=None,
                context_processors=None,
                template_object_name='object',
                mimetype=None):
    """
    Generic list of objects.

    Templates: ``<app_label>/<model_name>_list.html``
    Context:
        object_list
            list of objects
        is_paginated
            are the results paginated?
        results_per_page
            number of objects per page (if paginated)
        has_next
            is there a next page?
        has_previous
            is there a prev page?
        page
            the current page
        next
            the next page
        previous
            the previous page
        pages
            number of pages, total
        hits
            number of objects, total
        last_on_page
            the result number of the last of object in the
            object_list (1-indexed)
        first_on_page
            the result number of the first object in the
            object_list (1-indexed)
        page_range:
            A list of the page numbers (1-indexed).
    """
    if extra_context is None: extra_context = {}
    queryset = queryset._clone()
    if paginate_by:
        paginator = QuerySetPaginator(queryset,
                                      paginate_by,
                                      allow_empty_first_page=allow_empty)
        if not page:
            page = request.GET.get('page', 1)
        try:
            page_number = int(page)
        except ValueError:
            if page == 'last':
                page_number = paginator.num_pages
            else:
                # Page is not 'last', nor can it be converted to an int.
                raise Http404
        try:
            page_obj = paginator.page(page_number)
        except InvalidPage:
            raise Http404
        c = RequestContext(
            request,
            {
                '%s_list' % template_object_name: page_obj.object_list,
                'paginator': paginator,
                'page_obj': page_obj,

                # Legacy template context stuff. New templates should use page_obj
                # to access this instead.
                'is_paginated': page_obj.has_other_pages(),
                'results_per_page': paginator.per_page,
                'has_next': page_obj.has_next(),
                'has_previous': page_obj.has_previous(),
                'page': page_obj.number,
                'next': page_obj.next_page_number(),
                'previous': page_obj.previous_page_number(),
                'first_on_page': page_obj.start_index(),
                'last_on_page': page_obj.end_index(),
                'pages': paginator.num_pages,
                'hits': paginator.count,
                'page_range': paginator.page_range,
            },
            context_processors)
    else:
        c = RequestContext(
            request, {
                '%s_list' % template_object_name: queryset,
                'paginator': None,
                'page_obj': None,
                'is_paginated': False,
            }, context_processors)
        if not allow_empty and len(queryset) == 0:
            raise Http404
    for key, value in extra_context.items():
        if callable(value):
            c[key] = value()
        else:
            c[key] = value
    if not template_name:
        model = queryset.model
        template_name = "%s/%s_list.html" % (model._meta.app_label,
                                             model._meta.object_name.lower())
    t = template_loader.get_template(template_name)
    return HttpResponse(t.render(c), mimetype=mimetype)
Exemple #40
0
class DataGrid(object):
    """
    A representation of a list of objects, sorted and organized by
    columns. The sort order and column lists can be customized. allowing
    users to view this data however they prefer.

    This is meant to be subclassed for specific uses. The subclasses are
    responsible for defining one or more column types. It can also set
    one or more of the following optional variables:

        * 'title':                  The title of the grid.
        * 'profile_sort_field':     The variable name in the user profile
                                    where the sort order can be loaded and
                                    saved.
        * 'profile_columns_field":  The variable name in the user profile
                                    where the columns list can be loaded and
                                    saved.
        * 'paginate_by':            The number of items to show on each page
                                    of the grid. The default is 50.
        * 'paginate_orphans':       If this number of objects or fewer are
                                    on the last page, it will be rolled into
                                    the previous page. The default is 3.
        * 'page':                   The page to display. If this is not
                                    specified, the 'page' variable passed
                                    in the URL will be used, or 1 if that is
                                    not specified.
        * 'listview_template':      The template used to render the list view.
                                    The default is 'datagrid/listview.html'
        * 'column_header_template': The template used to render each column
                                    header. The default is
                                    'datagrid/column_header.html'
        * 'cell_template':          The template used to render a cell of
                                    data. The default is 'datagrid/cell.html'
        * 'optimize_sorts':         Whether or not to optimize queries when
                                    using multiple sorts. This can offer a
                                    speed improvement, but may need to be
                                    turned off for more advanced querysets
                                    (such as when using extra()).
                                    The default is True.
    """
    _columns = None

    @classmethod
    def add_column(cls, column):
        """Adds a new column for this datagrid.

        This can be used to add columns to a DataGrid subclass after
        the subclass has already been defined.

        The column added must have a unique ID already set.
        """
        cls._populate_columns()

        if not column.id:
            raise KeyError(
                'Custom datagrid columns must have a unique id attribute.')

        if column.id in _column_registry[cls]:
            raise KeyError('"%s" is already a registered column for %s'
                           % (column.id, cls.__name__))

        _column_registry[cls][column.id] = column

    @classmethod
    def remove_column(cls, column):
        """Removes a column from this datagrid.

        This can be used to remove columns previously added through
        add_column().
        """
        cls._populate_columns()

        try:
            del _column_registry[cls][column.id]
        except KeyError:
            raise KeyError('"%s" is not a registered column for %s'
                           % (column.id, cls.__name__))

    @classmethod
    def get_column(cls, column_id):
        """Returns the column with the given ID.

        If not found, this will return None.
        """
        cls._populate_columns()

        return _column_registry[cls].get(column_id)

    @classmethod
    def get_columns(cls):
        """Returns the list of registered columns for this datagrid."""
        cls._populate_columns()

        return six.itervalues(_column_registry[cls])

    @classmethod
    def _populate_columns(cls):
        """Populates the default list of columns for the datagrid.

        The default list contains all columns added in the class definition.
        """
        if cls not in _column_registry:
            _column_registry[cls] = {}

            for key in dir(cls):
                column = getattr(cls, key)

                if isinstance(column, Column):
                    column.id = key

                    if not column.field_name:
                        column.field_name = column.id

                    if not column.db_field:
                        column.db_field = column.field_name

                    cls.add_column(column)

    def __init__(self, request, queryset=None, title="", extra_context={},
                 optimize_sorts=True):
        self.request = request
        self.queryset = queryset
        self.rows = []
        self.columns = []
        self.column_map = {}
        self.id_list = []
        self.paginator = None
        self.page = None
        self.sort_list = None
        self.state_loaded = False
        self.page_num = 0
        self.id = None
        self.extra_context = dict(extra_context)
        self.optimize_sorts = optimize_sorts
        self.special_query_args = []

        if not hasattr(request, "datagrid_count"):
            request.datagrid_count = 0

        self.id = "datagrid-%s" % request.datagrid_count
        request.datagrid_count += 1

        # Customizable variables
        self.title = title
        self.profile_sort_field = None
        self.profile_columns_field = None
        self.paginate_by = 50
        self.paginate_orphans = 3
        self.listview_template = 'datagrid/listview.html'
        self.column_header_template = 'datagrid/column_header.html'
        self.cell_template = 'datagrid/cell.html'
        self.paginator_template = 'datagrid/paginator.html'

    @cached_property
    def cell_template_obj(self):
        obj = get_template(self.cell_template)

        if not obj:
            logging.error("Unable to load template '%s' for datagrid "
                          "cell. This may be an installation issue.",
                          self.cell_template,
                          extra={
                              'request': self.request,
                          })

        return obj

    @cached_property
    def column_header_template_obj(self):
        obj = get_template(self.column_header_template)

        if not obj:
            logging.error("Unable to load template '%s' for datagrid "
                          "column headers. This may be an installation "
                          "issue.",
                          self.column_header_template,
                          extra={
                              'request': self.request,
                          })

        return obj

    @property
    def all_columns(self):
        """Returns all columns in the datagrid, sorted by label."""
        return [
            self.get_stateful_column(column)
            for column in sorted(self.get_columns(),
                                 key=lambda x: x.detailed_label)
        ]

    def get_stateful_column(self, column):
        """Returns a StatefulColumn for the given Column instance.

        If one has already been created, it will be returned.
        """
        if column not in self.column_map:
            self.column_map[column] = StatefulColumn(self, column)

        return self.column_map[column]

    def load_state(self, render_context=None):
        """
        Loads the state of the datagrid.

        This will retrieve the user-specified or previously stored
        sorting order and columns list, as well as any state a subclass
        may need.
        """
        if self.state_loaded:
            return

        profile_sort_list = None
        profile_columns_list = None
        profile = None
        profile_dirty = False

        # Get the saved settings for this grid in the profile. These will
        # work as defaults and allow us to determine if we need to save
        # the profile.
        if self.request.user.is_authenticated():
            try:
                profile = self.request.user.get_profile()

                if self.profile_sort_field:
                    profile_sort_list = \
                        getattr(profile, self.profile_sort_field, None)

                if self.profile_columns_field:
                    profile_columns_list = \
                        getattr(profile, self.profile_columns_field, None)
            except SiteProfileNotAvailable:
                pass
            except ObjectDoesNotExist:
                pass

        # Figure out the columns we're going to display
        # We're also going to calculate the column widths based on the
        # shrink and expand values.
        colnames_str = self.request.GET.get('columns', profile_columns_list)

        if colnames_str:
            colnames = colnames_str.split(',')
        else:
            colnames = self.default_columns
            colnames_str = ",".join(colnames)

        expand_columns = []
        normal_columns = []

        for colname in colnames:
            column_def = self.get_column(colname)

            if not column_def:
                # The user specified a column that doesn't exist. Skip it.
                continue

            column = self.get_stateful_column(column_def)
            self.columns.append(column)
            column.active = True

            if column.expand:
                # This column is requesting all remaining space. Save it for
                # later so we can tell how much to give it. Each expanded
                # column will count as two normal columns when calculating
                # the normal sized columns.
                expand_columns.append(column)
            elif column.shrink:
                # Make this as small as possible.
                column.width = 0
            else:
                # We'll divide the column widths equally after we've built
                # up the lists of expanded and normal sized columns.
                normal_columns.append(column)

        self.columns[-1].last = True

        # Try to figure out the column widths for each column.
        # We'll start with the normal sized columns.
        total_pct = 100

        # Each expanded column counts as two normal columns.
        normal_column_width = total_pct / (len(self.columns) +
                                           len(expand_columns))

        for column in normal_columns:
            column.width = normal_column_width
            total_pct -= normal_column_width

        if len(expand_columns) > 0:
            expanded_column_width = total_pct / len(expand_columns)
        else:
            expanded_column_width = 0

        for column in expand_columns:
            column.width = expanded_column_width

        # Now get the sorting order for the columns.
        sort_str = self.request.GET.get('sort', profile_sort_list)

        if sort_str:
            self.sort_list = sort_str.split(',')
        else:
            self.sort_list = self.default_sort
            sort_str = ",".join(self.sort_list)

        # A subclass might have some work to do for loading and saving
        # as well.
        if self.load_extra_state(profile):
            profile_dirty = True

        # Now that we have all that, figure out if we need to save new
        # settings back to the profile.
        if profile:
            if (self.profile_columns_field and
                    colnames_str != profile_columns_list):
                setattr(profile, self.profile_columns_field, colnames_str)
                profile_dirty = True

            if self.profile_sort_field and sort_str != profile_sort_list:
                setattr(profile, self.profile_sort_field, sort_str)
                profile_dirty = True

            if profile_dirty:
                profile.save()

        self.state_loaded = True

        # Fetch the list of objects and have it ready.
        self.precompute_objects(render_context)

    def load_extra_state(self, profile):
        """
        Loads any extra state needed for this grid.

        This is used by subclasses that may have additional data to load
        and save. This should return True if any profile-stored state has
        changed, or False otherwise.
        """
        return False

    def precompute_objects(self, render_context=None):
        """
        Builds the queryset and stores the list of objects for use in
        rendering the datagrid.
        """
        query = self.queryset
        use_select_related = False

        # Generate the actual list of fields we'll be sorting by
        sort_list = []
        for sort_item in self.sort_list:
            if sort_item[0] == "-":
                base_sort_item = sort_item[1:]
                prefix = "-"
            else:
                base_sort_item = sort_item
                prefix = ""

            if sort_item:
                column = self.get_column(base_sort_item)
                if not column:
                    logging.warning('Skipping non-existing sort column "%s" '
                                    'for user "%s".',
                                    base_sort_item, self.request.user.username)
                    continue

                stateful_column = self.get_stateful_column(column)

                if stateful_column:
                    try:
                        sort_field = stateful_column.get_sort_field()
                    except Exception as e:
                        logging.error('Error when calling get_sort_field for '
                                      'DataGrid Column %r: %s',
                                      column, e, exc_info=1)
                        sort_field = ''

                    sort_list.append(prefix + sort_field)

                    # Lookups spanning tables require that we query from those
                    # tables. In order to keep things simple, we'll just use
                    # select_related so that we don't have to figure out the
                    # table relationships. We only do this if we have a lookup
                    # spanning tables.
                    if '.' in sort_field:
                        use_select_related = True

        if sort_list:
            query = query.order_by(*sort_list)

        query = self.post_process_queryset(query)

        self.paginator = QuerySetPaginator(query.distinct(), self.paginate_by,
                                           self.paginate_orphans)

        page_num = self.request.GET.get('page', 1)

        # Accept either "last" or a valid page number.
        if page_num == "last":
            page_num = self.paginator.num_pages

        try:
            self.page = self.paginator.page(page_num)
        except InvalidPage:
            raise Http404

        self.id_list = []

        if self.optimize_sorts and len(sort_list) > 0:
            # This can be slow when sorting by multiple columns. If we
            # have multiple items in the sort list, we'll request just the
            # IDs and then fetch the actual details from that.
            self.id_list = list(self.page.object_list.values_list(
                'pk', flat=True))

            # Make sure to unset the order. We can't meaningfully order these
            # results in the query, as what we really want is to keep it in
            # the order specified in id_list, and we certainly don't want
            # the database to do any special ordering (possibly slowing things
            # down). We'll set the order properly in a minute.
            self.page.object_list = self.post_process_queryset(
                self.queryset.model.objects.filter(
                    pk__in=self.id_list).order_by())

        if use_select_related:
            self.page.object_list = \
                self.page.object_list.select_related(depth=1)

        if self.id_list:
            # The database will give us the items in a more or less random
            # order, since it doesn't know to keep it in the order provided by
            # the ID list. This will place the results back in the order we
            # expect.
            index = dict([(id, pos) for (pos, id) in enumerate(self.id_list)])
            object_list = [None] * len(self.id_list)

            for obj in list(self.page.object_list):
                object_list[index[obj.pk]] = obj
        else:
            # Grab the whole list at once. We know it won't be too large,
            # and it will prevent one query per row.
            object_list = list(self.page.object_list)

        for column in self.columns:
            column.collect_objects(object_list)

        if render_context is None:
            render_context = self._build_render_context()

        try:
            self.rows = []

            for obj in object_list:
                if obj is None:
                    continue

                if hasattr(obj, 'get_absolute_url'):
                    obj_url = obj.get_absolute_url()
                else:
                    obj_url = None

                render_context['_datagrid_object_url'] = obj_url

                self.rows.append({
                    'object': obj,
                    'cells': [column.render_cell(obj, render_context)
                              for column in self.columns]
                })
        except Exception as e:
            logging.error('Error when calling render_cell for DataGrid '
                          'Column %r: %s',
                          column, e, exc_info=1)

    def post_process_queryset(self, queryset):
        """Add column-specific data to the queryset.

        Individual columns can define additional joins and extra info to add on
        to the queryset. This handles adding all of those.
        """
        for column in self.columns:
            try:
                queryset = column.augment_queryset(queryset)
            except Exception as e:
                logging.error('Error when calling augment_queryset for '
                              'DataGrid Column %r: %s',
                              column, e, exc_info=1)

        return queryset

    def render_listview(self, render_context=None):
        """
        Renders the standard list view of the grid.

        This can be called from templates.
        """
        try:
            if render_context is None:
                render_context = self._build_render_context()

            self.load_state(render_context)

            context = {
                'datagrid': self,
            }

            context.update(self.extra_context)
            context.update(render_context)

            return mark_safe(render_to_string(self.listview_template,
                                              Context(context)))
        except Exception:
            trace = traceback.format_exc()
            logging.error('Failed to render datagrid:\n%s' % trace,
                          extra={
                              'request': self.request,
                          })
            return mark_safe('<pre>%s</pre>' % trace)

    def render_listview_to_response(self, request=None, render_context=None):
        """
        Renders the listview to a response, preventing caching in the
        process.
        """
        response = HttpResponse(
            six.text_type(self.render_listview(render_context)))
        patch_cache_control(response, no_cache=True, no_store=True, max_age=0,
                            must_revalidate=True)
        return response

    def render_to_response(self, template_name, extra_context={}):
        """
        Renders a template containing this datagrid as a context variable.
        """
        render_context = self._build_render_context()
        self.load_state(render_context)

        # If the caller is requesting just this particular grid, return it.
        if self.request.GET.get('gridonly', False) and \
           self.request.GET.get('datagrid-id', None) == self.id:
            return self.render_listview_to_response(
                render_context=render_context)

        context = {
            'datagrid': self
        }
        context.update(extra_context)
        context.update(render_context)

        return render_to_response(template_name, Context(context))

    def render_paginator(self, adjacent_pages=3):
        """Renders the paginator for the datagrid.

        This can be called from templates.
        """
        extra_query = get_url_params_except(self.request.GET,
                                            'page', 'gridonly',
                                            *self.special_query_args)

        page_nums = range(max(1, self.page.number - adjacent_pages),
                          min(self.paginator.num_pages,
                              self.page.number + adjacent_pages)
                          + 1)

        if extra_query:
            extra_query += '&'

        context = {
            'is_paginated': self.page.has_other_pages(),
            'hits': self.paginator.count,
            'results_per_page': self.paginate_by,
            'page': self.page.number,
            'pages': self.paginator.num_pages,
            'page_numbers': page_nums,
            'has_next': self.page.has_next(),
            'has_previous': self.page.has_previous(),
            'show_first': 1 not in page_nums,
            'show_last': self.paginator.num_pages not in page_nums,
            'extra_query': extra_query,
        }

        if self.page.has_next():
            context['next'] = self.page.next_page_number()
        else:
            context['next'] = None

        if self.page.has_previous():
            context['previous'] = self.page.previous_page_number()
        else:
            context['previous'] = None

        context.update(self.extra_context)

        return mark_safe(render_to_string(self.paginator_template,
                                          Context(context)))

    def _build_render_context(self):
        """Builds a dictionary containing RequestContext contents.

        A RequestContext can be expensive, so it's best to reuse the
        contents of one when possible. This is not easy with a standard
        RequestContext, but it's possible to build one and then pull out
        the contents into a dictionary.
        """
        request_context = RequestContext(self.request)
        render_context = {}

        for d in request_context:
            render_context.update(d)

        return render_context

    @staticmethod
    def link_to_object(state, obj, value):
        return obj.get_absolute_url()

    @staticmethod
    def link_to_value(state, obj, value):
        return value.get_absolute_url()
Exemple #41
0
def browse(request, order_id):
    order = get_object_or_404(Order, id=order_id)
    Product = order.get_product_model()
    try:
        paginate_by=int(fetch_http_param(request, 'paginate_by', 25))
    except ValueError:
        raise Http404
    try:
        page_number = int(fetch_http_param(request, 'page', 1))
    except ValueError:
        raise Http404
    search = fetch_http_param(request, 'search', None)
    just_basket = fetch_http_param(request, 'just_basket', None)
    just_splittables = fetch_http_param(request, 'just_splittables', None)
    brand_slug = fetch_http_param(request, 'brand', None)
    code = fetch_http_param(request, 'code', None)
    productCategory_slug = request.GET.get('productCategory', None)
    productCategory_slug = fetch_http_param(request, 'productCategory', None)
    try:
        productCategory = ProductCategory.objects.get(slug=productCategory_slug, catalogue=order.catalogue)
    except ProductCategory.DoesNotExist:
        productCategory = None

    kwargs={'catalogue':order.catalogue}
    if brand_slug:
        kwargs['brand_slug'] = brand_slug
    if productCategory:
        kwargs['category'] = productCategory
    if code:
        kwargs['supplier_product_code__istartswith']=code
    if search:
        searchBits=search.split()
    else: searchBits=[]

    if just_basket:
        # This is probably a bit crap:
        basket_item_ids = [i.product.id for i in request.user.get_profile().basket_set.get(order=order).basketitem_set.all()]
        if basket_item_ids:
            filteredProducts = Product.objects.filter(id__in=basket_item_ids)
        else:
            filteredProducts = Product.objects.exclude(id__gt=0)
    else:
        filteredProducts = Product.objects.filter(**kwargs)

    for s in searchBits:
        filteredProducts = filteredProducts.filter(Q(name__icontains=s) | Q(category__name__icontains=s) | Q(brand_name__icontains=s))
    if just_splittables:
        filteredProducts = filteredProducts.filter(outgoingUnitsPerIncomingUnit__gte=2)
    paginator = QuerySetPaginator(filteredProducts, paginate_by)

    page = paginator.page(page_number)

    url_params = { 'brand': brand_slug,
              'productCategory':productCategory_slug,
              'code':code,
              'search':search,
              'paginate_by':str(paginate_by),
              'just_basket':just_basket,
              'page':str(page_number),
              }
    current_url = request.path+'?'+make_querystring(url_params)
    url_params['page'] = str(page_number-1)
    previous_url = request.path+'?'+make_querystring(url_params)
    url_params['page'] = str(page_number+1)
    next_url = request.path+'?'+make_querystring(url_params)

    brands = cache.get('catalogue_%d_brands' % order.catalogue.id)
    if not brands:
        # Refresh brands list...
        cursor = connection.cursor()
        cursor.execute("""select brand_slug, brand_name from catalogue_baseproduct where brand_slug != '' and catalogue_id=%s group by brand_slug, brand_name order by brand_name""", (order.catalogue.id,))

        brands = cursor.fetchall()
        cache.set('catalogue_%d_brands' % order.catalogue.id, brands, 60*60*24*1)

    return render_to_response('catalogue/browse.html', {
            'order' : order,
            'productCategories' : ProductCategory.objects.filter(catalogue=order.catalogue),
            'productCategory' : productCategory,
            'brand' : brand_slug,
            'brands' : brands,
            'search' : search,
            'paginator' : paginator,
            'page': page,
            'next_url':next_url,
            'previous_url':previous_url,
            'current_url':current_url,
            'code' : code,
            'just_basket' : just_basket,
            'total_basket_price':total_basket_price(request.user.username, order.id),
    }, context_instance=RequestContext(request) )
Exemple #42
0
class DataGrid(object):
    """
    A representation of a list of objects, sorted and organized by
    columns. The sort order and column lists can be customized. allowing
    users to view this data however they prefer.

    This is meant to be subclassed for specific uses. The subclasses are
    responsible for defining one or more column types. It can also set
    one or more of the following optional variables:

        * 'title':                  The title of the grid.
        * 'profile_sort_field':     The variable name in the user profile
                                    where the sort order can be loaded and
                                    saved.
        * 'profile_columns_field":  The variable name in the user profile
                                    where the columns list can be loaded and
                                    saved.
        * 'paginate_by':            The number of items to show on each page
                                    of the grid. The default is 50.
        * 'paginate_orphans':       If this number of objects or fewer are
                                    on the last page, it will be rolled into
                                    the previous page. The default is 3.
        * 'page':                   The page to display. If this is not
                                    specified, the 'page' variable passed
                                    in the URL will be used, or 1 if that is
                                    not specified.
        * 'listview_template':      The template used to render the list view.
                                    The default is 'datagrid/listview.html'
        * 'column_header_template': The template used to render each column
                                    header. The default is
                                    'datagrid/column_header.html'
        * 'cell_template':          The template used to render a cell of
                                    data. The default is 'datagrid/cell.html'
        * 'optimize_sorts':         Whether or not to optimize queries when
                                    using multiple sorts. This can offer a
                                    speed improvement, but may need to be
                                    turned off for more advanced querysets
                                    (such as when using extra()).
                                    The default is True.
    """
    _columns = None

    @classmethod
    def add_column(cls, column):
        """Adds a new column for this datagrid.

        This can be used to add columns to a DataGrid subclass after
        the subclass has already been defined.

        The column added must have a unique ID already set.
        """
        cls._populate_columns()

        if not column.id:
            raise KeyError(
                'Custom datagrid columns must have a unique id attribute.')

        if column.id in _column_registry[cls]:
            raise KeyError('"%s" is already a registered column for %s' %
                           (column.id, cls.__name__))

        _column_registry[cls][column.id] = column

    @classmethod
    def remove_column(cls, column):
        """Removes a column from this datagrid.

        This can be used to remove columns previously added through
        add_column().
        """
        cls._populate_columns()

        try:
            del _column_registry[cls][column.id]
        except KeyError:
            raise KeyError('"%s" is not a registered column for %s' %
                           (column.id, cls.__name__))

    @classmethod
    def get_column(cls, column_id):
        """Returns the column with the given ID.

        If not found, this will return None.
        """
        cls._populate_columns()

        return _column_registry[cls].get(column_id)

    @classmethod
    def get_columns(cls):
        """Returns the list of registered columns for this datagrid."""
        cls._populate_columns()

        return six.itervalues(_column_registry[cls])

    @classmethod
    def _populate_columns(cls):
        """Populates the default list of columns for the datagrid.

        The default list contains all columns added in the class definition.
        """
        if cls not in _column_registry:
            _column_registry[cls] = {}

            for key in dir(cls):
                column = getattr(cls, key)

                if isinstance(column, Column):
                    column.id = key

                    if not column.field_name:
                        column.field_name = column.id

                    if not column.db_field:
                        column.db_field = column.field_name

                    cls.add_column(column)

    def __init__(self,
                 request,
                 queryset=None,
                 title="",
                 extra_context={},
                 optimize_sorts=True):
        self.request = request
        self.queryset = queryset
        self.rows = []
        self.columns = []
        self.column_map = {}
        self.id_list = []
        self.paginator = None
        self.page = None
        self.sort_list = None
        self.state_loaded = False
        self.page_num = 0
        self.id = None
        self.extra_context = dict(extra_context)
        self.optimize_sorts = optimize_sorts
        self.special_query_args = []

        if not hasattr(request, "datagrid_count"):
            request.datagrid_count = 0

        self.id = "datagrid-%s" % request.datagrid_count
        request.datagrid_count += 1

        # Customizable variables
        self.title = title
        self.profile_sort_field = None
        self.profile_columns_field = None
        self.paginate_by = 50
        self.paginate_orphans = 3
        self.listview_template = 'datagrid/listview.html'
        self.column_header_template = 'datagrid/column_header.html'
        self.cell_template = 'datagrid/cell.html'
        self.paginator_template = 'datagrid/paginator.html'

    @cached_property
    def cell_template_obj(self):
        obj = get_template(self.cell_template)

        if not obj:
            logging.error(
                "Unable to load template '%s' for datagrid "
                "cell. This may be an installation issue.",
                self.cell_template,
                extra={
                    'request': self.request,
                })

        return obj

    @cached_property
    def column_header_template_obj(self):
        obj = get_template(self.column_header_template)

        if not obj:
            logging.error(
                "Unable to load template '%s' for datagrid "
                "column headers. This may be an installation "
                "issue.",
                self.column_header_template,
                extra={
                    'request': self.request,
                })

        return obj

    @property
    def all_columns(self):
        """Returns all columns in the datagrid, sorted by label."""
        return [
            self.get_stateful_column(column)
            for column in sorted(self.get_columns(),
                                 key=lambda x: x.detailed_label)
        ]

    def get_stateful_column(self, column):
        """Returns a StatefulColumn for the given Column instance.

        If one has already been created, it will be returned.
        """
        if column not in self.column_map:
            self.column_map[column] = StatefulColumn(self, column)

        return self.column_map[column]

    def load_state(self, render_context=None):
        """
        Loads the state of the datagrid.

        This will retrieve the user-specified or previously stored
        sorting order and columns list, as well as any state a subclass
        may need.
        """
        if self.state_loaded:
            return

        profile_sort_list = None
        profile_columns_list = None
        profile = None
        profile_dirty = False

        # Get the saved settings for this grid in the profile. These will
        # work as defaults and allow us to determine if we need to save
        # the profile.
        if self.request.user.is_authenticated():
            try:
                profile = self.request.user.get_profile()

                if self.profile_sort_field:
                    profile_sort_list = \
                        getattr(profile, self.profile_sort_field, None)

                if self.profile_columns_field:
                    profile_columns_list = \
                        getattr(profile, self.profile_columns_field, None)
            except SiteProfileNotAvailable:
                pass
            except ObjectDoesNotExist:
                pass

        # Figure out the columns we're going to display
        # We're also going to calculate the column widths based on the
        # shrink and expand values.
        colnames_str = self.request.GET.get('columns', profile_columns_list)

        if colnames_str:
            colnames = colnames_str.split(',')
        else:
            colnames = self.default_columns
            colnames_str = ",".join(colnames)

        expand_columns = []
        normal_columns = []

        for colname in colnames:
            column_def = self.get_column(colname)

            if not column_def:
                # The user specified a column that doesn't exist. Skip it.
                continue

            column = self.get_stateful_column(column_def)
            self.columns.append(column)
            column.active = True

            if column.expand:
                # This column is requesting all remaining space. Save it for
                # later so we can tell how much to give it. Each expanded
                # column will count as two normal columns when calculating
                # the normal sized columns.
                expand_columns.append(column)
            elif column.shrink:
                # Make this as small as possible.
                column.width = 0
            else:
                # We'll divide the column widths equally after we've built
                # up the lists of expanded and normal sized columns.
                normal_columns.append(column)

        self.columns[-1].last = True

        # Try to figure out the column widths for each column.
        # We'll start with the normal sized columns.
        total_pct = 100

        # Each expanded column counts as two normal columns.
        normal_column_width = total_pct / (len(self.columns) +
                                           len(expand_columns))

        for column in normal_columns:
            column.width = normal_column_width
            total_pct -= normal_column_width

        if len(expand_columns) > 0:
            expanded_column_width = total_pct / len(expand_columns)
        else:
            expanded_column_width = 0

        for column in expand_columns:
            column.width = expanded_column_width

        # Now get the sorting order for the columns.
        sort_str = self.request.GET.get('sort', profile_sort_list)

        if sort_str:
            self.sort_list = sort_str.split(',')
        else:
            self.sort_list = self.default_sort
            sort_str = ",".join(self.sort_list)

        # A subclass might have some work to do for loading and saving
        # as well.
        if self.load_extra_state(profile):
            profile_dirty = True

        # Now that we have all that, figure out if we need to save new
        # settings back to the profile.
        if profile:
            if (self.profile_columns_field
                    and colnames_str != profile_columns_list):
                setattr(profile, self.profile_columns_field, colnames_str)
                profile_dirty = True

            if self.profile_sort_field and sort_str != profile_sort_list:
                setattr(profile, self.profile_sort_field, sort_str)
                profile_dirty = True

            if profile_dirty:
                profile.save()

        self.state_loaded = True

        # Fetch the list of objects and have it ready.
        self.precompute_objects(render_context)

    def load_extra_state(self, profile):
        """
        Loads any extra state needed for this grid.

        This is used by subclasses that may have additional data to load
        and save. This should return True if any profile-stored state has
        changed, or False otherwise.
        """
        return False

    def precompute_objects(self, render_context=None):
        """
        Builds the queryset and stores the list of objects for use in
        rendering the datagrid.
        """
        query = self.queryset
        use_select_related = False

        # Generate the actual list of fields we'll be sorting by
        sort_list = []
        for sort_item in self.sort_list:
            if sort_item[0] == "-":
                base_sort_item = sort_item[1:]
                prefix = "-"
            else:
                base_sort_item = sort_item
                prefix = ""

            if sort_item:
                column = self.get_column(base_sort_item)
                if not column:
                    logging.warning(
                        'Skipping non-existing sort column "%s" '
                        'for user "%s".', base_sort_item,
                        self.request.user.username)
                    continue

                stateful_column = self.get_stateful_column(column)

                if stateful_column:
                    try:
                        sort_field = stateful_column.get_sort_field()
                    except Exception as e:
                        logging.error(
                            'Error when calling get_sort_field for '
                            'DataGrid Column %r: %s',
                            column,
                            e,
                            exc_info=1)
                        sort_field = ''

                    sort_list.append(prefix + sort_field)

                    # Lookups spanning tables require that we query from those
                    # tables. In order to keep things simple, we'll just use
                    # select_related so that we don't have to figure out the
                    # table relationships. We only do this if we have a lookup
                    # spanning tables.
                    if '.' in sort_field:
                        use_select_related = True

        if sort_list:
            query = query.order_by(*sort_list)

        query = self.post_process_queryset(query)

        self.paginator = QuerySetPaginator(query.distinct(), self.paginate_by,
                                           self.paginate_orphans)

        page_num = self.request.GET.get('page', 1)

        # Accept either "last" or a valid page number.
        if page_num == "last":
            page_num = self.paginator.num_pages

        try:
            self.page = self.paginator.page(page_num)
        except InvalidPage:
            raise Http404

        self.id_list = []

        if self.optimize_sorts and len(sort_list) > 0:
            # This can be slow when sorting by multiple columns. If we
            # have multiple items in the sort list, we'll request just the
            # IDs and then fetch the actual details from that.
            self.id_list = list(
                self.page.object_list.values_list('pk', flat=True))

            # Make sure to unset the order. We can't meaningfully order these
            # results in the query, as what we really want is to keep it in
            # the order specified in id_list, and we certainly don't want
            # the database to do any special ordering (possibly slowing things
            # down). We'll set the order properly in a minute.
            self.page.object_list = self.post_process_queryset(
                self.queryset.model.objects.filter(
                    pk__in=self.id_list).order_by())

        if use_select_related:
            self.page.object_list = \
                self.page.object_list.select_related(depth=1)

        if self.id_list:
            # The database will give us the items in a more or less random
            # order, since it doesn't know to keep it in the order provided by
            # the ID list. This will place the results back in the order we
            # expect.
            index = dict([(id, pos) for (pos, id) in enumerate(self.id_list)])
            object_list = [None] * len(self.id_list)

            for obj in list(self.page.object_list):
                object_list[index[obj.pk]] = obj
        else:
            # Grab the whole list at once. We know it won't be too large,
            # and it will prevent one query per row.
            object_list = list(self.page.object_list)

        for column in self.columns:
            column.collect_objects(object_list)

        if render_context is None:
            render_context = self._build_render_context()

        try:
            self.rows = [{
                'object':
                obj,
                'cells': [
                    column.render_cell(obj, render_context)
                    for column in self.columns
                ]
            } for obj in object_list if obj is not None]
        except Exception as e:
            logging.error(
                'Error when calling render_cell for DataGrid '
                'Column %r: %s',
                column,
                e,
                exc_info=1)

    def post_process_queryset(self, queryset):
        """Add column-specific data to the queryset.

        Individual columns can define additional joins and extra info to add on
        to the queryset. This handles adding all of those.
        """
        for column in self.columns:
            try:
                queryset = column.augment_queryset(queryset)
            except Exception as e:
                logging.error(
                    'Error when calling augment_queryset for '
                    'DataGrid Column %r: %s',
                    column,
                    e,
                    exc_info=1)

        return queryset

    def render_listview(self, render_context=None):
        """
        Renders the standard list view of the grid.

        This can be called from templates.
        """
        try:
            if render_context is None:
                render_context = self._build_render_context()

            self.load_state(render_context)

            context = {
                'datagrid': self,
            }

            context.update(self.extra_context)
            context.update(render_context)

            return mark_safe(
                render_to_string(self.listview_template, Context(context)))
        except Exception:
            trace = traceback.format_exc()
            logging.error('Failed to render datagrid:\n%s' % trace,
                          extra={
                              'request': self.request,
                          })
            return mark_safe('<pre>%s</pre>' % trace)

    def render_listview_to_response(self, request=None, render_context=None):
        """
        Renders the listview to a response, preventing caching in the
        process.
        """
        response = HttpResponse(
            six.text_type(self.render_listview(render_context)))
        patch_cache_control(response,
                            no_cache=True,
                            no_store=True,
                            max_age=0,
                            must_revalidate=True)
        return response

    def render_to_response(self, template_name, extra_context={}):
        """
        Renders a template containing this datagrid as a context variable.
        """
        render_context = self._build_render_context()
        self.load_state(render_context)

        # If the caller is requesting just this particular grid, return it.
        if self.request.GET.get('gridonly', False) and \
           self.request.GET.get('datagrid-id', None) == self.id:
            return self.render_listview_to_response(
                render_context=render_context)

        context = {'datagrid': self}
        context.update(extra_context)
        context.update(render_context)

        return render_to_response(template_name, Context(context))

    def render_paginator(self, adjacent_pages=3):
        """Renders the paginator for the datagrid.

        This can be called from templates.
        """
        extra_query = get_url_params_except(self.request.GET, 'page',
                                            *self.special_query_args)

        page_nums = range(
            max(1, self.page.number - adjacent_pages),
            min(self.paginator.num_pages, self.page.number + adjacent_pages) +
            1)

        if extra_query:
            extra_query += '&'

        context = {
            'is_paginated': self.page.has_other_pages(),
            'hits': self.paginator.count,
            'results_per_page': self.paginate_by,
            'page': self.page.number,
            'pages': self.paginator.num_pages,
            'page_numbers': page_nums,
            'has_next': self.page.has_next(),
            'has_previous': self.page.has_previous(),
            'show_first': 1 not in page_nums,
            'show_last': self.paginator.num_pages not in page_nums,
            'extra_query': extra_query,
        }

        if self.page.has_next():
            context['next'] = self.page.next_page_number()
        else:
            context['next'] = None

        if self.page.has_previous():
            context['previous'] = self.page.previous_page_number()
        else:
            context['previous'] = None

        context.update(self.extra_context)

        return mark_safe(
            render_to_string(self.paginator_template, Context(context)))

    def _build_render_context(self):
        """Builds a dictionary containing RequestContext contents.

        A RequestContext can be expensive, so it's best to reuse the
        contents of one when possible. This is not easy with a standard
        RequestContext, but it's possible to build one and then pull out
        the contents into a dictionary.
        """
        request_context = RequestContext(self.request)
        render_context = {}

        for d in request_context:
            render_context.update(d)

        return render_context

    @staticmethod
    def link_to_object(state, obj, value):
        return obj.get_absolute_url()

    @staticmethod
    def link_to_value(state, obj, value):
        return value.get_absolute_url()
Exemple #43
0
def archive_month(request, year, month, queryset, date_field, paginate_by=None,
        page=None, month_format='%b', template_name=None, template_loader=loader,
        extra_context=None, allow_empty=False, context_processors=None,
        template_object_name='object', mimetype=None, allow_future=False):
    """
    Generic monthly archive view.
 
    A replacement for ``django.views.generic.date_based.archive_month``
    which offers result-set pagination.
 
    Templates: ``<app_label>/<model_name>_archive_month.html``
    Context:
        month:
            (date) this month
        next_month:
            (date) the first day of the next month, or None if the next month is in the future
        previous_month:
            (date) the first day of the previous month
        object_list:
            list of objects published in the given month
        paginator:
            ``django.core.paginator.Paginator`` object
            (Available only if paginate_by and page provided)
        page_obj:
            ``django.core.paginator.Page`` object
            (Available only if paginate_by and page provided)
    """
    if extra_context is None: extra_context = {}
    try:
        date = datetime.date(*time.strptime(year+month, '%Y'+month_format)[:3])
    except ValueError:
        raise Http404
 
    model = queryset.model
    now = datetime.datetime.now()
 
    # Calculate first and last day of month, for use in a date-range lookup.
    first_day = date.replace(day=1)
    if first_day.month == 12:
        last_day = first_day.replace(year=first_day.year + 1, month=1)
    else:
        last_day = first_day.replace(month=first_day.month + 1)
    lookup_kwargs = {'%s__range' % date_field: (first_day, last_day)}
 
    # Only bother to check current date if the month isn't in the past and future objects are requested.
    if last_day >= now.date() and not allow_future:
        lookup_kwargs['%s__lte' % date_field] = now
    object_list = queryset.filter(**lookup_kwargs)
    if not object_list and not allow_empty:
        raise Http404
 
    # Calculate the next month, if applicable.
    if allow_future:
        next_month = last_day + datetime.timedelta(days=1)
    elif last_day < datetime.date.today():
        next_month = last_day + datetime.timedelta(days=1)
    else:
        next_month = None
 
    if paginate_by:
        paginator = QuerySetPaginator(object_list, per_page=paginate_by, allow_empty_first_page=allow_empty)
        if not page:
            page = request.GET.get('page', 1)
        try:
            page_number = int(page)
        except ValueError:
            if page == 'last':
                page_number = paginator.num_pages
            else:
                raise Http404
        try:
            page_obj = paginator.page(page_number)
        except InvalidPage:
            raise Http404
        c = RequestContext(request, {
            'month': date,
            'next_month': next_month,
            'previous_month': first_day - datetime.timedelta(days=1),
            '%s_list' % template_object_name: page_obj.object_list,
            'paginator': paginator,
            'page_obj': page_obj,
        }, context_processors)
    else:
        c = RequestContext(request, {
            '%s_list' % template_object_name: object_list,
            'month': date,
            'next_month': next_month,
            'previous_month': first_day - datetime.timedelta(days=1),
            'paginator': None,
            'page_obj': None,
        }, context_processors)
 
    if not template_name:
        template_name = "%s/%s_archive_month.html" % (model._meta.app_label, model._meta.object_name.lower())
    t = template_loader.get_template(template_name)
 
    for key, value in extra_context.items():
        if callable(value):
            c[key] = value()
        else:
            c[key] = value
    return HttpResponse(t.render(c), mimetype=mimetype)
def json_browse(request, page_number=1):
    paginator = QuerySetPaginator(DownloadLink.objects.filter(hidden=False), 4)
    page = paginator.page(page_number)
    from time import sleep

    return json_paginator_response(page)
Exemple #45
0
def archive_week(request, year, week, queryset, date_field, paginate_by=None,
        page=None, template_name=None, template_loader=loader,
        extra_context=None, allow_empty=True, context_processors=None,
        template_object_name='object', mimetype=None, allow_future=False):
    """
    Generic weekly archive view.
 
    A replacement for ``django.views.generic.date_based.archive_week``
    which offers result-set pagination.
 
    Templates: ``<app_label>/<model_name>_archive_week.html``
    Context:
        week:
            (date) this week
        object_list:
            list of objects published in the given week
        paginator:
            ``django.core.paginator.Paginator`` object
            (Available only if paginate_by and page are provided)
        page_obj:
            ``django.core.paginator.Page`` object
            (Available only if paginate_by and page are provided)
    """
    if extra_context is None: extra_context = {}
    try:
        date = datetime.date(*time.strptime(year+'-0-'+week, '%Y-%w-%U')[:3])
    except ValueError:
        raise Http404
 
    model = queryset.model
    now = datetime.datetime.now()
 
    # Calculate first and last day of week, for use in a date-range lookup.
    first_day = date
    last_day = date + datetime.timedelta(days=7)
    lookup_kwargs = {'%s__range' % date_field: (first_day, last_day)}
 
    # Only bother to check current date if the week isn't in the past and future objects aren't requested.
    if last_day >= now.date() and not allow_future:
        lookup_kwargs['%s__lte' % date_field] = now
    object_list = queryset.filter(**lookup_kwargs)
    if not object_list and not allow_empty:
        raise Http404
 
    if paginate_by:
        paginator = QuerySetPaginator(object_list, per_page=paginate_by, allow_empty_first_page=allow_empty)
        if not page:
            page = request.GET.get('page', 1)
        try:
            page_number = int(page)
        except ValueError:
            if page == 'last':
                page_number = paginator.num_pages
            else:
                raise Http404
        try:
            page_obj = paginator.page(page_number)
        except InvalidPage:
            raise Http404
        c = RequestContext(request, {
            'week': date,
            '%s_list' % template_object_name: page_obj.object_list,
            'paginator': paginator,
            'page_obj': page_obj,
        }, context_processors)
    else:
        c = RequestContext(request, {
            '%s_list' % template_object_name: object_list,
            'week': date,
            'paginator': None,
            'page_obj': None,
        }, context_processors)
 
    if not template_name:
        template_name = "%s/%s_archive_week.html" % (model._meta.app_label, model._meta.object_name.lower())
    t = template_loader.get_template(template_name)
 
    for key, value in extra_context.items():
        if callable(value):
            c[key] = value()
        else:
            c[key] = value
    return HttpResponse(t.render(c), mimetype=mimetype)
Exemple #46
0
def archive_day(request, year, month, day, queryset, date_field,
        paginate_by=None, page=None,
        month_format='%b', day_format='%d', template_name=None,
        template_loader=loader, extra_context=None, allow_empty=False,
        context_processors=None, template_object_name='object',
        mimetype=None, allow_future=False):
    """
    Generic daily archive view.
 
    A replacement for ``django.views.generic.date_based.archive_day``
    which offers result-set pagination.
 
    Templates: ``<app_label>/<model_name>_archive_day.html``
    Context:
        object_list:
            list of objects published that day
        day:
            (datetime) the day
        previous_day
            (datetime) the previous day
        next_day
            (datetime) the next day, or None if the current day is today
        paginator:
            ``django.core.paginator.Paginator`` object
            (Available only if paginate_by and page are provided)
        page_obj:
            ``django.core.paginator.Page`` object
            (Available only if paginate_by and page are provided)
    """
    if extra_context is None: extra_context = {}
    try:
        date = datetime.date(*time.strptime(year+month+day, '%Y'+month_format+day_format)[:3])
    except ValueError:
        raise Http404
 
    model = queryset.model
    now = datetime.datetime.now()
 
    if isinstance(model._meta.get_field(date_field), DateTimeField):
        lookup_kwargs = {'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max))}
    else:
        lookup_kwargs = {date_field: date}
 
    # Only bother to check current date if the date isn't in the past and future objects aren't requested.
    if date >= now.date() and not allow_future:
        lookup_kwargs['%s__lte' % date_field] = now
    object_list = queryset.filter(**lookup_kwargs)
    if not allow_empty and not object_list:
        raise Http404
 
    # Calculate the next day, if applicable.
    if allow_future:
        next_day = date + datetime.timedelta(days=1)
    elif date < datetime.date.today():
        next_day = date + datetime.timedelta(days=1)
    else:
        next_day = None
 
    if paginate_by:
        paginator = QuerySetPaginator(object_list, per_page=paginate_by, allow_empty_first_page=allow_empty)
        if not page:
            page = request.GET.get('page', 1)
        try:
            page_number = int(page)
        except ValueError:
            if page == 'last':
                page_number = paginator.num_pages
            else:
                raise Http404
        try:
            page_obj = paginator.page(page_number)
        except InvalidPage:
            raise Http404
        c = RequestContext(request, {
            'day': date,
            'previous_day': date - datetime.timedelta(days=1),
            'next_day': next_day,
            '%s_list' % template_object_name: page_obj.object_list,
            'paginator': paginator,
            'page_obj': page_obj,
        }, context_processors)
    else:
        c = RequestContext(request, {
            '%s_list' % template_object_name: object_list,
            'day': date,
            'previous_day': date - datetime.timedelta(days=1),
            'next_day': next_day,
            'paginator': None,
            'page_obj': None,
        }, context_processors)
 
 
    if not template_name:
        template_name = "%s/%s_archive_day.html" % (model._meta.app_label, model._meta.object_name.lower())
    t = template_loader.get_template(template_name)
 
    for key, value in extra_context.items():
        if callable(value):
            c[key] = value()
        else:
            c[key] = value
    return HttpResponse(t.render(c), mimetype=mimetype)
Exemple #47
0
def archive_year(request, year, queryset, date_field, template_name=None,
        template_loader=loader, extra_context=None, allow_empty=False,
        context_processors=None, template_object_name='object', mimetype=None,
        make_object_list=False, allow_future=False, paginate_by=None, page=None):
    """
    Generic yearly archive view.
 
    A replacement for ``django.views.generic.date_based.archive_year``
    which offers result-set pagination.
 
    Templates: ``<app_label>/<model_name>_archive_year.html``
    Context:
        date_list
            List of months in this year with objects
        year
            This year
        object_list
            List of objects published in the given month
            (Only available if make_object_list argument is True)
        paginator
            ``django.core.paginator.Paginator`` object
            (Only available if make_object_list argument is True)
        page_obj
            ``django.core.paginator.Page`` object
            (Only available if make_object_list argument is True)
    """
    if extra_context is None: extra_context = {}
    model = queryset.model
    now = datetime.datetime.now()
 
    lookup_kwargs = {'%s__year' % date_field: year}
 
    # Only bother to check current date if the year isn't in the past and future objects aren't requested.
    if int(year) >= now.year and not allow_future:
        lookup_kwargs['%s__lte' % date_field] = now
    date_list = queryset.filter(**lookup_kwargs).dates(date_field, 'month')
    if not date_list and not allow_empty:
        raise Http404
 
    if make_object_list:
        object_list = queryset.filter(**lookup_kwargs)
        paginator = QuerySetPaginator(object_list, per_page=paginate_by, allow_empty_first_page=allow_empty)
        if not page:
            page = request.GET.get('page', 1)
        try:
            page_number = int(page)
        except ValueError:
            if page == 'last':
                page_number = paginator.num_pages
            else:
                raise Http404
        try:
            page_obj = paginator.page(page_number)
        except InvalidPage:
            raise Http404
        c = RequestContext(request, {
            'date_list': date_list,
            'year': year,
            '%s_list' % template_object_name: page_obj.object_list,
            'paginator': paginator,
            'page_obj': page_obj,
        }, context_processors)
    else:
        object_list = []
        c = RequestContext(request, {
            'date_list': date_list,
            'year': year,
            '%s_list' % template_object_name: object_list,
            'paginator': None,
            'page_obj': None,
        }, context_processors)
 
    if not template_name:
        template_name = "%s/%s_archive_year.html" % (model._meta.app_label, model._meta.object_name.lower())
    t = template_loader.get_template(template_name)
    for key, value in extra_context.items():
        if callable(value):
            c[key] = value()
        else:
            c[key] = value
    return HttpResponse(t.render(c), mimetype=mimetype)