It would be great if there's any way to change the format of the URL.
Maybe some people would prefer /foo/bar/page/42/ to /foo/bar/?page=42.
Comment #1
Posted on Jul 21, 2008 by Happy KangarooI'm not really sure how to easily go about doing this, although I like the concept. Any ideas?
Comment #2
Posted on Jul 30, 2008 by Happy PandaBeing pretty new to Python and Django, I figured this would be a good learning experience, so I decided to take a stab at it. The updates turned out to be not so bad. In short: implement process_view in the middleware to catch some info about how it should do the pagination and update the template tags to be able to output page-based URLs.
To make this work a few special keyword arguments need to be passed to the view, though they'll never actually get there. Here's an example URLconf:
(r'^example/page/(?P<page>\d+)/$', 'my_view', {'paginate': True, 'page_url':
'/example/page/%d/'})
The keywords involved here are:
paginate Serves no purpose other than to tell process_view that this is a view it should...process.
page_url Should define two groups: one before the page number and one after.
page Most likely will be defined as a named parameter in the URL.
Beyond that, everything else is the same.
When the page is requested but before the view is called, process_view will be triggered. It will inspect the keyword arguments being passed to the view, store them away in the request object, and then remove them so they don't make it to the view. This is similar to how process_request handles the 'page' GET argument.
When the autopagination tag is called, 'use_page_path' is set to True in the context if 'page_url' is defined on the request object. Later paginate uses it to determine whether to generate the new page-based URLs, or use the default ?page=n. A new tag, page_path, has been added to output the URLs using the page number provided to it. This works for prev, next, and everything inbetween.
All in all, it works pretty smoothly. The only things I'm not crazy about are:
Having to specify page_url. This definitely violates DRY, but I couldn't find a way to automatically detect the regex that triggered the view. The only other option, and how I originally wrote it, was to use a sort of reverse regex that would match something like r'^(./page/)\d+(.)$' (basically, everything but the page number) and then build the URL using the first group, the page number, and then the second group. It worked, but it felt a bit clunky and seemed like it would be confusing for anyone trying to use it.
The extra keyword arguments. It might be a bit cleaner if it were to use attributes on the view function rather than require the extra keywords. This would even allow for the use of a decorator to add a bit of extra sugar to it. 'page' would likely still have to be trapped by process_view, but with it generally being defined in the URL, that's fine.
@paginate('/example/page/%d/') def my_view(request): ...
Attached are a patch containing my changes as well as a demo project to show it in action. Visit /movies/ in the demo to see it generating the page-based URLs, or /movies/all/ for ?page=n.
- pagination_pretty_urls.diff 5.09KB
- demo_project.zip 43KB
Comment #3
Posted on Jul 30, 2008 by Happy PandaMy notes for the 'page_url' keyword were apparently written before I switched it back from the regex, so it makes mention of matching groups. It should read:
- page_url Should define the base URL for the page, with %d inserted where the page number should go.
Sorry about any confusion there.
Comment #4
Posted on Aug 3, 2008 by Massive CamelRight now I give each paginator in each template a varname and I set in the get params %(name)s_page to hold the current page and sometimes %(name)s_items to set the number of items, by this way you can have n-paginators per template. If you have more than one paginator per page, hard coding state in the urls becomes impossible. Besides this pitfall, when you need a list paginated why don't you use the django's generic view? because you will need to handle pagination in your view, task which does the generic view too. Also i'm not sure at all if after decorate the view and hack the urls in the middleware you will be able to use named urls and permalinks.
Comment #5
Posted on Aug 30, 2008 by Happy KangarooSince Google Code decided not to e-mail me about changes in this issue tracker, I'm just finding out about the responses to this now. I will review the patch and suggestions now. Thanks for the effort, guys!
Comment #6
Posted on Oct 2, 2008 by Happy HorseIn addition to these URLs being nicer, is it true, as the docs suggest, that the per-site cache middleware will not cache pages with querystrings? ("The cache middleware caches every page that doesn't have GET or POST parameters." -- http://docs.djangoproject.com/en/dev//topics/cache/#the-per-site-cache.) It would be nice to make paginated pages susceptible to per-site caching.
Comment #7
Posted on Oct 2, 2008 by Happy PandaWow, forgot all about this patch. Looks like someone else did, too. nudges floguy
jsmullyan - It's been a while since I've poked around in any of this, but it seems to me like that'd be quite possible if a) the pagination middleware was loaded before the cache middleware, and b) after tucking the GET parameter away in the request object (this is done already), it's deleted from request.REQUEST.
This makes two assumptions, and I haven't had a chance to look into either: 1. Middleware is executed in order that it's defined. I think this is true. 2. request.REQUEST is mutable and not a copy, and modifications made in pagination's process_request will carry over to the cache middleware so it doesn't ever know that a GET argument was ever present. I'd guess this to also be true, as the request object itself does keep the changes.
So, try loading the pagination middleware first and throw a "del(request.REQUEST['page']) after the assignment to request.page in middleware.py's process_request function, and let us know if it works. :)
Comment #8
Posted on Oct 4, 2008 by Happy Horserequest.REQUEST is a "MergeDict", which does a lookup on the GET and POST QueryDicts (which are immutable), so that wouldn't quite work (in fact, it doesn't support item deletion). Also, even if you did make request.GET temporarily immutable and took "page" out, it would be essential for the page value to somehow become part of the cache key. But if I understand your patch correctly, I think it would address this problem, by getting the page value out of the querystring entirely.
Comment #9
Posted on Oct 24, 2008 by Happy KangarooI'm sorry, but after thinking about this further, I don't think it's a good idea for this app. While I do like the idea of optionally adding page information into the url itself instead of the query parameters, I don't think it's possible to integrate into this library in a non-ugly way. I think that if we want this type of functionality, it's going to be necessary to deal with the pagination information in the view and not solely in the template.
There are certainly ways to achieve this, like intercepting the url before it gets to the url dispatcher somehow, but I really don't like that solution for some of the reasons that you outlined above. Unless someone comes up with a solution that has fewer drawbacks (I don't know how that solution would go), I don't see a solution to this problem coming any time soon in this app.
Comment #10
Posted on Jul 31, 2009 by Quick RabbitHave you looked at how it is implemented here?
http://blog.awarelabs.com/2009/digg-style-pagination-in-django-revisited/
Comment #11
Posted on Feb 21, 2012 by Quick OxComment deleted
Status: WontFix
Labels:
Type-Defect
Priority-Medium