def get_links(self): """Returns all links used in the payload. By default, this only includes pagination links. Subclasses can provide additional links. """ links = {} full_path = self.request.build_absolute_uri(self.request.path) query_parameters = get_url_params_except( self.request.GET, self.start_param, self.max_results_param) if query_parameters: query_parameters = '&' + query_parameters if self.has_prev(): links[self.prev_key] = { 'method': 'GET', 'href': self.build_pagination_url( full_path, self.get_prev_index(), self.max_results, query_parameters), } if self.has_next(): links[self.next_key] = { 'method': 'GET', 'href': self.build_pagination_url( full_path, self.get_next_index(), self.max_results, query_parameters), } return links
def __init__(self, request, queryset, results_key="results", prev_key="prev", next_key="next", total_results_key="total_results", default_max_results=25, max_results_cap=200, serialize_object_func=None, extra_data={}, *args, **kwargs): try: start = max(int(request.GET.get('start', 0)), 0) except ValueError: start = 0 try: max_results = \ min(int(request.GET.get('max-results', default_max_results)), max_results_cap) except ValueError: max_results = default_max_results results = queryset[start:start + max_results] total_results = queryset.count() if total_results == 0: results = [] elif serialize_object_func: results = [serialize_object_func(obj) for obj in results] else: results = list(results) data = { results_key: results, total_results_key: total_results, } data.update(extra_data) full_path = request.build_absolute_uri(request.path) query_parameters = get_url_params_except(request.GET, 'start', 'max-results') if query_parameters: query_parameters = '&' + query_parameters if start > 0: data['links'][prev_key] = { 'method': 'GET', 'href': '%s?start=%s&max-results=%s%s' % (full_path, max(start - max_results, 0), max_results, query_parameters), } if start + len(results) < total_results: data['links'][next_key] = { 'method': 'GET', 'href': '%s?start=%s&max-results=%s%s' % (full_path, start + max_results, max_results, query_parameters), } WebAPIResponse.__init__(self, request, obj=data, *args, **kwargs)
def querystring_with(context, attr, value): """Helper for querystring manipulation. Often, we'll want to update only a single value in a querystring without changing the others. This tag helps with that. """ existing_query = get_url_params_except(context['request'].GET, attr) new_query = urlencode({attr.encode('utf-8'): value.encode('utf-8')}) if existing_query: return '?%s&%s' % (existing_query, new_query) else: return '?%s' % new_query
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) extra_query = get_url_params_except(self.request.GET, 'page') 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, '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, '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) 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 querystring_with(context, attr, value): """Return the current page URL with a new query string argument added. This makes it easy to add to or replace part of a query string for the current page's URL, which may already contain a query string. If the page URL already has a query string, a new item is added in the form of ``&attr=value``. If it doesn't have a query string, this will start a new one in the form of ``?attr=value``. If the attribute already exists in the query string, its value will be replaced. Args: context (django.template.context.RequestContext): The Django template rendering context. attr (unicode): The name of the attribute for the new query string argument. value (unicode): The value of the attribute for the new query string argument. Returns: unicode: The new URL with the modified query string. Example: .. code-block:: html+django <a href="{% querystring_with "sorted" "1" %}">Sort</a> """ warnings.warn( '{%% querystring_with "%(attr)s" "%(value)s" %%} is deprecated and ' 'will be removed in a future version of Djblets. Please use ' '{%% querystring "mode" "%(attr)s=%(value)s" %%} instead.' % { 'attr': attr, 'value': value, }, RemovedInDjblets30Warning) existing_query = get_url_params_except(context['request'].GET, attr) new_query = urlencode({attr.encode('utf-8'): value.encode('utf-8')}) if existing_query: result = '?%s&%s' % (existing_query, new_query) else: result = '?%s' % new_query return escape(result)
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 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 get_toggle_url(self, state): """ Returns the URL of the current page with this column's visibility toggled. """ columns = [column.id for column in state.datagrid.columns] if state.active: try: columns.remove(self.id) except ValueError: pass else: columns.append(self.id) url_params = get_url_params_except(state.datagrid.request.GET, 'columns') if url_params: url_params = url_params + '&' return "?%scolumns=%s" % (url_params, ",".join(columns))
def querystring_with(context, attr, value): """Return the current page URL with a new query string argument added. This makes it easy to add to or replace part of a query string for the current page's URL, which may already contain a query string. If the page URL already has a query string, a new item is added in the form of ``&attr=value``. If it doesn't have a query string, this will start a new one in the form of ``?attr=value``. If the attribute already exists in the query string, its value will be replaced. Args: attr (unicode): The name of the attribute for the new query string argument. value (unicode): The value of the attribute for the new query string argument. Returns: unicode: The new URL with the modified query string. Example: .. code-block:: html+django <a href="{% querystring_with "sorted" "1" %}">Sort</a> """ existing_query = get_url_params_except(context['request'].GET, attr) new_query = urlencode({attr.encode('utf-8'): value.encode('utf-8')}) if existing_query: result = '?%s&%s' % (existing_query, new_query) else: result = '?%s' % new_query return escape(result)
def get_header(self, state): """ Displays a sortable column header. The column header will include the current sort indicator, if it belongs in the sort list. It will also be made clickable in order to modify the sort order appropriately, if sortable. """ datagrid = state.datagrid in_sort = False sort_direction = self.SORT_DESCENDING sort_primary = False sort_url = "" unsort_url = "" if self.sortable: sort_list = list(datagrid.sort_list) if sort_list: rev_column_id = "-%s" % self.id new_column_id = self.id cur_column_id = "" if self.id in sort_list: # This column is currently being sorted in # ascending order. sort_direction = self.SORT_ASCENDING cur_column_id = self.id new_column_id = rev_column_id elif rev_column_id in sort_list: # This column is currently being sorted in # descending order. sort_direction = self.SORT_DESCENDING cur_column_id = rev_column_id new_column_id = self.id if cur_column_id: in_sort = True sort_primary = (sort_list[0] == cur_column_id) if not sort_primary: # If this is not the primary column, we want to keep # the sort order intact. new_column_id = cur_column_id # Remove this column from the current location in the list # so we can move it to the front of the list. sort_list.remove(cur_column_id) # Insert the column name into the beginning of the sort list. sort_list.insert(0, new_column_id) else: # There's no sort list to begin with. Make this column # the only entry. sort_list = [self.id] # We can only support two entries in the sort list, so truncate # this. del(sort_list[2:]) url_params = get_url_params_except( datagrid.request.GET, "sort", "datagrid-id", "gridonly", "columns") if url_params: url_params = url_params + '&' url_prefix = "?%ssort=" % url_params unsort_url = url_prefix + ','.join(sort_list[1:]) sort_url = url_prefix + ','.join(sort_list) ctx = Context({ 'column': self, 'column_state': state, 'in_sort': in_sort, 'sort_ascending': sort_direction == self.SORT_ASCENDING, 'sort_primary': sort_primary, 'sort_url': sort_url, 'unsort_url': unsort_url, }) return mark_safe(datagrid.column_header_template_obj.render(ctx))
def get_header(self, state): """ Displays a sortable column header. The column header will include the current sort indicator, if it belongs in the sort list. It will also be made clickable in order to modify the sort order appropriately, if sortable. """ datagrid = state.datagrid in_sort = False sort_direction = self.SORT_DESCENDING sort_primary = False sort_url = "" unsort_url = "" if self.sortable: sort_list = list(datagrid.sort_list) if sort_list: rev_column_id = "-%s" % self.id new_column_id = self.id cur_column_id = "" if self.id in sort_list: # This column is currently being sorted in # ascending order. sort_direction = self.SORT_ASCENDING cur_column_id = self.id new_column_id = rev_column_id elif rev_column_id in sort_list: # This column is currently being sorted in # descending order. sort_direction = self.SORT_DESCENDING cur_column_id = rev_column_id new_column_id = self.id if cur_column_id: in_sort = True sort_primary = (sort_list[0] == cur_column_id) if not sort_primary: # If this is not the primary column, we want to keep # the sort order intact. new_column_id = cur_column_id # Remove this column from the current location in the list # so we can move it to the front of the list. sort_list.remove(cur_column_id) # Insert the column name into the beginning of the sort list. sort_list.insert(0, new_column_id) else: # There's no sort list to begin with. Make this column # the only entry. sort_list = [self.id] # We can only support two entries in the sort list, so truncate # this. del (sort_list[2:]) url_params = get_url_params_except(datagrid.request.GET, "sort", "datagrid-id", "gridonly", "columns") if url_params: url_params = url_params + '&' url_prefix = "?%ssort=" % url_params unsort_url = url_prefix + ','.join(sort_list[1:]) sort_url = url_prefix + ','.join(sort_list) ctx = Context({ 'column': self, 'column_state': state, 'in_sort': in_sort, 'sort_ascending': sort_direction == self.SORT_ASCENDING, 'sort_primary': sort_primary, 'sort_url': sort_url, 'unsort_url': unsort_url, }) return mark_safe(datagrid.column_header_template_obj.render(ctx))