To enable "clean url" mode, install [django-pagination](http://code.google.com/p/django-pagination/),
then just add to settings.py
PAGINATION_CLEAN_URL = True
and replace '.../pagination/templatetags/pagination_tags.py' with follow, and so
add to 'pagination/templates/pagination/' folder
pagination_clean_url.html
file, with following code
**Note:**
in urls.py
should be like this:
url(r'^/foo/bar/\d*/?$', foobar_list_handle),
By default every time you change and save an object in the admin, the change_list "jumps" to the first page, so filters you used to find the object (or the pagination-page) have to be applied again. If you have to go through a multi-object-list step-by-step this could become really annoying.
The above snippet changes this behaviour by returning to the referring URL when saving. Included in this URL are variables for the filters/pagination.
The snippet is part of your custom Model.admin in admin.py.
This class makes easier the job of rendering lists of model instances in django templates. It's intended to mimic the behavior of the Model Forms in that it contains the code needed to render it as an HTML table and makes it easy to handle all the model lists from a single view (as it's usually done with the generic views for creating and updating model instances).
It also supports pagination and provides hooks for subclassing and customizing the rendered fields, column titles and list order.
Basic example:
`class Account(Model):`
`name = models.CharField(max_length=MAX_LENGTH)`
`responsible = models.CharField(max_length=MAX_LENGTH)`
`email = models.EmailField()`
`class AccountModelList(ModelList):`
`class Meta:`
`model = Account`
`fields = ['name', 'responsible'] #email won't get a column`
The model list would be instantiated with something like:
`model_list = AccountModelList(instances=account_queryset)`
Then a table header can be rendered with model_list.as_table_header(), while the table rows can be rendered calling as_table() on each model_list.items element.
ModelPagination
Designed and Coded by Cal Leeming
Many thanks to Harry Roberts for giving us a heads up on how to do this properly!
----------------------------------------------------------------------------
This is a super optimized way of paginating datasets over 1 million records.
It uses MAX() rather then COUNT(), because this is super faster.
EXAMPLE:
>>> _t = time.time(); x = Post.objects.aggregate(Max('id')); "Took %ss"%(time.time() - _t )
'Took 0.00103402137756s'
>>> _t = time.time(); x = Post.objects.aggregate(Count('id')); "Took %ss"%(time.time() - _t )
'Took 0.92404794693s'
>>>
This does mean that if you go deleting things, then the IDs won't be accurate,
so if you delete 50 rows, you're exact count() isn't going to match, but this is
okay for pagination, because for SEO, we want items to stay on the original page
they were scanned on. If you go deleting items, then the items shift backwards
through the pages, so you end up with inconsistent SEO on archive pages. If this
doesn't make sense, go figure it out for yourself, its 2am in the morning ffs ;p
Now, the next thing we do, is use id seeking, rather then OFFSET, because again,
this is a shitton faster:
EXAMPLE:
>>> _t = time.time(); x = map(lambda x: x, Post.objects.filter(id__gte=400000, id__lt=400500).all()); print "Took %ss"%(time.time() - _t)
Took 0.0467309951782s
>>> _t = time.time(); _res = map(lambda x: x, Post.objects.all()[400000:400500]); print "Took %ss"%(time.time() - _t)
Took 1.05785298347s
>>>
By using this seeking method (which btw, can be implemented on anything, not just pagination)
on a table with 5 million rows, we are saving 0.92s on row count, and 1.01s on item grabbing.
This may not seem like much, but if you have 1024 concurrent users, this will make a huge
difference.
If you have any questions or problems, feel free to contact me on
cal.leeming [at] simplicitymedialtd.co.uk
Django Pagination Template Tag that allows unlimited customization to the current Django Pagination.
Automatically creates template variables that can be used to display the pagination in any format that you can desire such as
Previous 1 2 3 [ 4 ] 5 6 7 Next
First Previous 12 14 15 16 17 [ 18 ] 19 20 22 25 Next Last
Showing 25 of 80 Results
First Page 23 27 30 33 [ 36 ] 38 41 44 50 Next Last
This is a simple URI Querystring generator that can be used with Django for generating URI Querystring and preserving the current
Currently working to port into a template tag to allow
{% urlgen page|5 %} {% urlgen page 5 %} {% urlgen page,5 %}
OR
{% urlgen sort name display all %} etc..
Search engines might conclude there's duplicate content if `/some_view/` and `/some_view/?page=1` returns the same results. This middleware redirects `?page=1` to the URL without the page parameter. You can set the name of the parameter in settings.py as `PAGE_VAR`.
See [here](http://www.muhuk.com/2009/08/a-civilized-way-display-lots-of-data/) for more details.
This snippet helps preserving query parameters such as page number when the view perform redirects.
It does not support hooking templates and contexts currently.
This tag is designed to facilitate pagination in the case where both the page number and other parameters (eg. search criteria) are passed via GET.
It takes one argument - a dictionary of GET variables to be added to the current url
Example usage:
{% for page_num in results.paginator.page_range %}
<a href="{% append_to_get p=page_num %}">{{ page_num }}</a>
{% endfor %}
Note that the passed arguments are evaluated within the template context.
This snippet shows a way to preserve GET arguments with pagination. Many people make mistakes to omit the query arguments besides page arguments for the pagination, and making sure correct may sphagettize your code.
My take on digg-like pagination.
Save the code as 'templatetags/pagination_nav.py' in one of your apps.
It relies on a 'pagination_nav.html' template. Here is a base template:
{% if pages %}
<div class="bottom-pagination-nav">
{% if previous_url %}<a href="{{ previous_url }}">{% else %}<span>{% endif %}« Previous{% if previous_url %}</a>{% else %}</span>{% endif %}
{% for group in pages %}
{% for page in group %}
{% if page.current %}<span>{{ page.number }}</span>{% else %}<a href="{{ page.url }}">{{ page.number }}</a>{% endif %}
{% endfor %}
{% if not forloop.last %}<span>...</span>{% endif %}
{% endfor %}
{% if next_url %}<a href="{{ next_url }}">{% else %}<span>{% endif %}Next »{% if next_url %}</a>{% else %}</span>{% endif %}
</div>
{% endif %}
**Update:**
Never mind. See [dc's comment](http://www.djangosnippets.org/snippets/1391/#c1763) below for a much easier way to do this.
I recently had to write a template for a paginated view which displayed a serial number for each `object` in the `object_list`. I normally use `forloop.counter` for general purpose serial numbers. However this did not work with paginated views as the counter gets reset in each page. This caused the serial numbers to go from 1 to #-of-results-in-the-page and then repeat.
**Assumptions:**
The `adjust_for_pagination` filter adjusts the value of `forloop.counter` based on the current page. `Page` and `is_paginated` variables are expected to be present in the context. These should respectively denote the current page number (1 based) and if the results are paginated. `RESULTS_PER_PAGE` is currently taken from the settings file. I couldn't think of a way to pass this value also from the template.
This allows you to create an alphabetical filter for a list of objects; e.g. `Browse by title: A-G H-N O-Z`. See [this entry](http://developer.yahoo.com/ypatterns/pattern.php?pattern=alphafilterlinks) in Yahoo's design pattern library for more info.
NamePaginator works like Django's Paginator. You pass in a list of objects and how many you want per letter range ("page"). Then, it will dynamically generate the "pages" so that there are approximately `per_page` objects per page.
By dynamically generating the letter ranges, you avoid having too many objects in some letter ranges and too few in some. If your list is heavy on one end of the letter range, there will be more pages for that range.
It splits the pages on letter boundaries, so not all the pages will have exactly `per_page` objects. However, it will decide to overflow or underflow depending on which is closer to `per_page`.
**NamePaginator Arguments**:
`object_list`: A list, dictionary, QuerySet, or something similar.
`on`: If you specified a QuerySet, this is the field it will paginate on. In the example below, we're paginating a list of Contact objects, but the `Contact.email` string is what will be used in filtering.
`per_page`: How many items you want per page.
**Examples:**
>>> paginator = NamePaginator(Contacts.objects.all(), \
... on="email", per_page=10)
>>> paginator.num_pages
4
>>> paginator.pages
[A, B-R, S-T, U-Z]
>>> paginator.count
36
>>> page = paginator.page(2)
>>> page
'B-R'
>>> page.start_letter
'B'
>>> page.end_letter
'R'
>>> page.number
2
>>> page.count
8
In your view, you have something like:
contact_list = Contacts.objects.all()
paginator = NamePaginator(contact_list, \
on="first_name", per_page=25)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
page = paginator.page(page)
except (InvalidPage):
page = paginator.page(paginator.num_pages)
return render_to_response('list.html', {"page": page})
In your template, have something like:
{% for object in page.object_list %}
...
{% endfor %}
<div class="pagination">
Browse by title:
{% for p in page.paginator.pages %}
{% if p == page %}
<span class="selected">{{ page }}</span>
{% else %}
<a href="?page={{ page.number }}">
{{ page }}
</a>
{% endif %}
{% endfor %}
</div>
It currently only supports paginating on alphabets (not alphanumeric) and will throw an exception if any of the strings it is paginating on are blank. You can fix either of those shortcomings pretty easily, though.