Changeset 3735

Show
Ignore:
Timestamp:
11/19/07 21:17:32 (1 year ago)
Author:
roger
Message:

Fix #1617:

  • Paginate decorator ensures page number is between 1 and page_count.
  • Adapt PaginateDataGrid? template to make use of paginate.href_xxxx attributes.
  • Introduce 2 new parameters: paginate.redirect_on_out_of_range and paginate.redirect_on_last_page.
  • + Tests
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/1.0/CHANGELOG.txt

    r3720 r3735  
    88    * Introduction of tg.mochikit_suppress to prevent the inclusion of 
    99      the shipped MochiKit 1.3.1. That allows to include custom mochikit versions. 
     10    * PaginateDataGrid template now makes use of paginate attributes to render 
     11      the links for first/previous/next/last page. 
     12    * paginate.href_last returns a special URL that allows paginate decorator 
     13      to compute the correct last page number at server-side. 
    1014 
    1115*Features* 
     
    1822    * The tg-admin quickstart command has now an option --svn allowing 
    1923      automatic creation of the project in the specified SVN repository. 
     24    * Introduction of paginate.redirect_on_out_of_range and 
     25      paginate.redirect_on_last_page, which determine if paginate decorator 
     26      should raise a redirect when current page is out of bound and the 
     27      last page is requested, respectively. 
    2028 
    2129*Fixes* 
     
    2533    * Tickets #1458, #1599 and #1604. Fixed SQLAlchemy and Elixir issues. 
    2634    * Ticket #1608. Fixed issues with tg-admin update on a project in SVN. 
    27     * Ticket #1582: Fix pagination of SQLAlchemy Query ordering by backrefs  
    28       and synonyms' attributes. It also supports SQLAlchemy 0.4.1, which  
     35    * Ticket #1582. Fix pagination of SQLAlchemy Query ordering by backrefs 
     36      and synonyms' attributes. It also supports SQLAlchemy 0.4.1, which 
    2937      removed the "properties" accessor on Mapper. 
    3038    * Ticket #1605. Ensure paginate links bring the same result when using 
    3139      ordering columns with a custom datagrid template. 
    32      
     40    * Ticket #1617. Fix pagination of out of bound pages. 
     41 
    3342 
    3443*Contributors* 
  • branches/1.0/turbogears/paginate.py

    r3718 r3735  
    2626 
    2727import turbogears 
     28from turbogears.controllers import redirect 
    2829from turbogears.decorator import weak_signature_decorator 
    2930from turbogears.view import variable_providers 
     
    8788                return default 
    8889 
    89             page = int(kwpop(1, var_name + '_tgp_no', 'tg_paginate_no')) 
     90            get = turbogears.config.get 
     91 
     92            page = kwpop(1, var_name + '_tgp_no', 'tg_paginate_no') 
     93            if page == 'last': 
     94                page = None 
     95            else: 
     96                page = int(page) 
     97                if page < 1: 
     98                    page = 1 
     99                    if get('paginate.redirect_on_out_of_range'): 
     100                        cherrypy.request.params[var_name + '_tgp_no'] = page 
     101                        redirect(cherrypy.request.path, cherrypy.request.params) 
     102 
    90103            limit_ = int( 
    91104                kwpop(limit, var_name + '_tgp_limit', 'tg_paginate_limit')) 
     
    169182                            var_data)) 
    170183 
     184            page_count = int(ceil(float(row_count)/limit_)) 
     185 
     186            if page > page_count: 
     187                page = max(page_count, 1) 
     188                if get('paginate.redirect_on_out_of_range'): 
     189                    cherrypy.request.params[var_name + '_tgp_no'] = page 
     190                    redirect(cherrypy.request.path, cherrypy.request.params) 
     191 
     192            if page is None: 
     193                page = max(page_count, 1) 
     194                if get('paginate.redirect_on_last_page'): 
     195                    cherrypy.request.params[var_name + '_tgp_no'] = page 
     196                    redirect(cherrypy.request.path, cherrypy.request.params) 
     197 
    171198            offset = (page-1) * limit_ 
    172             page_count = int(ceil(float(row_count)/limit_)) 
    173199 
    174200            # if it's possible display every page 
     
    211237 
    212238            global _simulate_offset 
    213             get = turbogears.config.get 
    214239            if _simulate_offset is None: 
    215240                _simulate_offset = get('paginate.simulate_offset', None) 
     
    263288        self.ordering = ordering 
    264289        self.row_count = row_count 
    265         self.first_item = (current_page - 1) * limit + 1 
     290        self.first_item = page_count and ((current_page - 1) * limit + 1) or 0 
    266291        self.last_item = min(current_page * limit, row_count) 
    267292        self.reversed = False 
     
    287312                self.input_values) 
    288313            self.input_values.update({ 
    289                 var_name + '_tgp_no': page_count
     314                var_name + '_tgp_no': 'last'
    290315                var_name + '_tgp_limit': limit 
    291316            }) 
  • branches/1.0/turbogears/qstemplates/quickstart/+package+/config/app.cfg_tmpl

    r3577 r3735  
    4040# Set to True if the scheduler should be started 
    4141# tg.scheduler = False 
     42 
     43# Set to True to allow paginate decorator redirects when page number gets 
     44# out of bound. Useful for getting the real page id in the url 
     45# paginate.redirect_on_out_of_range = True 
     46 
     47# Set to True to allow paginate decorator redirects when last page is requested. 
     48# This is useful for getting the real last page id in the url 
     49# paginate.redirect_on_last_page = True 
    4250 
    4351# Set session or cookie 
  • branches/1.0/turbogears/tests/test_paginate.py

    r3716 r3735  
    55 
    66from turbogears import config, expose, database 
    7 from turbogears.controllers import RootController 
     7from turbogears.controllers import RootController, url 
    88from turbogears.database import get_engine, metadata, session, mapper 
    99from turbogears.paginate import paginate, sort_ordering 
     
    8585            " , ".join(["%s=%r" % (k,v) for k,v in paginate.__dict__.items()]) 
    8686 
    87     def assert_ok(body, key, value): 
     87    def assert_ok(body, key, value, raw=False): 
    8888        assert "ok: [paginate" in body 
    89         expr = "%s=%r " % (key, value) 
     89        if raw: 
     90            expr = "%s=%s " % (key, value) 
     91        else: 
     92            expr = "%s=%r " % (key, value) 
    9093        if expr not in body: 
    9194            print body 
     
    161164        body = cherrypy.response.body[0] 
    162165        assert "fail: paginate does not have 'foobar' attribute" in body 
     166 
     167    def test_raw_expectation(self): 
     168        create_request('/spy_correct_expectation') 
     169        Spy.assert_ok(cherrypy.response.body[0], 'var_name', 'data') 
     170        Spy.assert_ok(cherrypy.response.body[0], 'var_name', "'data'", raw=True) 
    163171 
    164172 
     
    250258        multiple = expose()(multiple) 
    251259 
     260        def empty(self): 
     261            spy = Spy(var_name='data', pages=[], limit=4, page_count=0, 
     262                      order=None, ordering={}, row_count=0, 
     263                      current_page=1, first_item=0, last_item=0) 
     264            data = [] 
     265            return dict(data=data, spy=spy) 
     266        empty = paginate("data", limit=4)(empty) 
     267        empty = expose()(empty) 
     268 
    252269 
    253270    def setUp(self): 
     
    375392        assert '"bar": ["U", "V", "W", "X", "Y", "Z"]' in self.body 
    376393 
    377     # TODO: paginate.input_values 
    378  
    379     # TODO: paginate.href_XXX 
    380  
    381     # TODO: test 'out of bound' pages 
     394    def test_out_of_bound_pages(self): 
     395        for number in range(-3,1): 
     396            self.request("/basic/?tg_paginate_no=%s" % number) 
     397            assert '"data": [0, 1, 2, 3]' in self.body 
     398            Spy.assert_ok(self.body, 'current_page', 1) 
     399            Spy.assert_ok(self.body, 'first_item', 1) 
     400            Spy.assert_ok(self.body, 'last_item', 4) 
     401 
     402        for number in range(4, 7): 
     403            self.request("/basic/?tg_paginate_no=%s" % number) 
     404            print self.body 
     405            assert '"data": [8, 9]' in self.body 
     406            Spy.assert_ok(self.body, 'current_page', 3) 
     407            Spy.assert_ok(self.body, 'first_item', 9) 
     408            Spy.assert_ok(self.body, 'last_item', 10) 
     409 
     410    def test_last_page(self): 
     411        self.request("/basic/?tg_paginate_no=last") 
     412        print self.body 
     413        assert '"data": [8, 9]' in self.body 
     414        Spy.assert_ok(self.body, 'current_page', 3) 
     415        Spy.assert_ok(self.body, 'first_item', 9) 
     416        Spy.assert_ok(self.body, 'last_item', 10) 
     417 
     418    def test_href(self): 
     419        self.request("/basic/?data_tgp_no=0") # out of bound 
     420        Spy.assert_ok(self.body, 'current_page', 1) 
     421        Spy.assert_ok(self.body, 'href_first', None) 
     422        Spy.assert_ok(self.body, 'href_prev', None) 
     423        Spy.assert_ok(self.body, 'href_next', r"'\/basic\/?data_tgp_no=2&data_tgp_limit=4'", raw=True) 
     424        Spy.assert_ok(self.body, 'href_last', r"'\/basic\/?data_tgp_no=last&data_tgp_limit=4'", raw=True) 
     425 
     426        self.request("/basic/?data_tgp_no=1") 
     427        Spy.assert_ok(self.body, 'current_page', 1) 
     428        Spy.assert_ok(self.body, 'href_first', None) 
     429        Spy.assert_ok(self.body, 'href_prev', None) 
     430        Spy.assert_ok(self.body, 'href_next', r"'\/basic\/?data_tgp_no=2&data_tgp_limit=4'", raw=True) 
     431        Spy.assert_ok(self.body, 'href_last', r"'\/basic\/?data_tgp_no=last&data_tgp_limit=4'", raw=True) 
     432 
     433        self.request("/basic/?data_tgp_no=2") 
     434        Spy.assert_ok(self.body, 'current_page', 2) 
     435        Spy.assert_ok(self.body, 'href_first', r"'\/basic\/?data_tgp_no=1&data_tgp_limit=4'", raw=True) 
     436        Spy.assert_ok(self.body, 'href_prev', r"'\/basic\/?data_tgp_no=1&data_tgp_limit=4'", raw=True) 
     437        Spy.assert_ok(self.body, 'href_next', r"'\/basic\/?data_tgp_no=3&data_tgp_limit=4'", raw=True) 
     438        Spy.assert_ok(self.body, 'href_last', r"'\/basic\/?data_tgp_no=last&data_tgp_limit=4'", raw=True) 
     439 
     440        self.request("/basic/?data_tgp_no=3") 
     441        Spy.assert_ok(self.body, 'current_page', 3) 
     442        Spy.assert_ok(self.body, 'href_first', r"'\/basic\/?data_tgp_no=1&data_tgp_limit=4'", raw=True) 
     443        Spy.assert_ok(self.body, 'href_prev', r"'\/basic\/?data_tgp_no=2&data_tgp_limit=4'", raw=True) 
     444        Spy.assert_ok(self.body, 'href_next', None) 
     445        Spy.assert_ok(self.body, 'href_last', None) 
     446 
     447        self.request("/basic/?data_tgp_no=4") # out of bound ! 
     448        Spy.assert_ok(self.body, 'current_page', 3) 
     449        Spy.assert_ok(self.body, 'href_first', r"'\/basic\/?data_tgp_no=1&data_tgp_limit=4'", raw=True) 
     450        Spy.assert_ok(self.body, 'href_prev', r"'\/basic\/?data_tgp_no=2&data_tgp_limit=4'", raw=True) 
     451        Spy.assert_ok(self.body, 'href_next', None) 
     452        Spy.assert_ok(self.body, 'href_last', None) 
     453 
     454        # empty data 
     455        self.request("/empty") 
     456        Spy.assert_ok(self.body, 'current_page', 1) 
     457        Spy.assert_ok(self.body, 'href_first', None) 
     458        Spy.assert_ok(self.body, 'href_prev', None) 
     459        Spy.assert_ok(self.body, 'href_next', None) 
     460        Spy.assert_ok(self.body, 'href_last', None) 
     461 
     462    def test_empty_data(self): 
     463        self.request("/empty") 
     464        print self.body 
     465        assert '"data": []' in self.body 
     466 
    382467 
    383468 
  • branches/1.0/turbogears/widgets/templates/paginate_datagrid.kid

    r1743 r3735  
    11<div xmlns:py="http://purl.org/kid/ns#"> 
    2      
    3   <table>   
     2  <table> 
    43    <tr> 
    54        <td> 
     
    87                    <td width="50" align="right"> 
    98                        &nbsp; 
    10                         <span py:if="not tg.paginate.current_page == 1"> 
    11                             <a href="${tg.paginate.get_href(1)}">&lt;&lt;</a> 
    12                             <a href="${tg.paginate.get_href(tg.paginate.current_page-1)}">&lt;</a> 
     9                        <span py:if="tg.paginate.href_prev"> 
     10                            <a href="${tg.paginate.href_first}">&lt;&lt;</a> 
     11                            <a href="${tg.paginate.href_prev}">&lt;</a> 
    1312                        </span> 
    1413                    </td> 
     
    2221                    </td> 
    2322                    <td width="50"> 
    24                         <span py:if="tg.paginate.pages and not tg.paginate.current_page == tg.paginate.page_count"> 
    25                             <a href="${tg.paginate.get_href(tg.paginate.current_page+1)}">&gt;</a> 
    26                             <a href="${tg.paginate.get_href(tg.paginate.page_count)}">&gt;&gt;</a> 
     23                        <span py:if="tg.paginate.href_next"> 
     24                            <a href="${tg.paginate.href_next}">&gt;</a> 
     25                            <a href="${tg.paginate.href_last}">&gt;&gt;</a> 
    2726                        </span> 
    2827                        &nbsp; 
    2928                    </td> 
    30                     </tr>     
    31             </table>         
     29                    </tr> 
     30            </table> 
    3231        </td> 
    3332    </tr> 
     
    3736            <thead py:if="columns"> 
    3837              <th py:for="i, col in enumerate(columns)" class="col_${i}"> 
    39                 <a py:if="col.get_option('sortable', False) and getattr(tg, 'paginate', False)"  
     38                <a py:if="col.get_option('sortable', False) and getattr(tg, 'paginate', False)" 
    4039                    href="${tg.paginate.get_href(1, col.name, col.get_option('reverse_order', False))}">${col.title}</a> 
    4140                <span py:if="not getattr(tg, 'paginate', False) or not col.get_option('sortable', False)" py:replace="col.title"/>