Skip to content

Problem with endpoints which have multiple routes #20

@hieu-n

Description

@hieu-n

My packages' version:

Flask (0.12)
Flask-Bootstrap (3.3.7.1)
flask-nav (0.6)

I have a function with multiple routes as below:

from flask_nav.elements import Navbar, View
from flask import Flask
from flask_nav import Nav

nav = Nav()
app = Flask(__name__)
nav.init_app(app)


@app.route('/<profile>/trang-<int:page>/order-<order>-<order_dir>/', )
@app.route('/<profile>/', defaults={'page': 1, 'order': 'sequence', 'order_dir': 'asc'}, )
def index(profile, page, order, order_dir):
    ...


@nav.navigation()
def mk_navbar():
    ...

    return Navbar(
        ...
        View(u'Home', 'index', profile=pf),
        ...
    )

When I tried to access http://localhost:5000/kkm/trang-1/order-list_price-asc/, I got the exception as below:

Traceback (most recent call last):
...
  File "/miniconda2/envs/flecom/lib/python2.7/site-packages/flask_nav/elements.py", line 24, in render
    self))
  File "/miniconda2/envs/flecom/lib/python2.7/site-packages/visitor/__init__.py", line 48, in visit
    return meth(node)
  File "/miniconda2/envs/flecom/lib/python2.7/site-packages/flask_bootstrap/nav.py", line 53, in visit_Navbar
    bar_list.add(self.visit(item))
  File "/miniconda2/envs/flecom/lib/python2.7/site-packages/visitor/__init__.py", line 48, in visit
    return meth(node)
  File "/miniconda2/envs/flecom/lib/python2.7/site-packages/flask_bootstrap/nav.py", line 99, in visit_View
    if node.active:
  File "/miniconda2/envs/flecom/lib/python2.7/site-packages/flask_nav/elements.py", line 89, in active
    append_unknown=not self.ignore_query)
  File "/miniconda2/envs/flecom/lib/python2.7/site-packages/werkzeug/routing.py", line 806, in build
    add(self._converters[data].to_url(values[data]))
KeyError: 'page'

As I tried to debug, I find the problem may lie in active method in flask_nav/elements.py (line 89):

    @property
    def active(self):
        if not request.endpoint == self.endpoint:
            return False

        # rebuild the url and compare results. we can't rely on using get_url()
        # because whether or not an external url is created depends on factors
        # outside our control

        _, url = request.url_rule.build(self.url_for_kwargs,
                                        append_unknown=not self.ignore_query)

        if self.ignore_query:
            return url == request.path

        # take query string into account.
        # FIXME: ensure that the order of query parameters is consistent
        return url == request.full_path

I set a breakpoint at _, url = request.url_rule.build(self.url_for_kwargs, and inspected some variables:

>>>  request.endpoint
'index'
>>> self.endpoint
'index'
>>> request.url_rule
'<Rule \\'/<profile>/trang-<page>/order-<order>-<order_dir>/\\' (HEAD, OPTIONS, GET) -> index>'       
>>> self.url_for_kwargs
{'profile': u'kkm'}

So the rule requires profile, page, order and order_dir. But url_for_kwargs only has profile.
If I change:

        _, url = request.url_rule.build(self.url_for_kwargs,
                                        append_unknown=not self.ignore_query)

to:

       url = url_for(request.endpoint, **self.url_for_kwargs, )

It would work:

>>> url = url_for(request.endpoint, **self.url_for_kwargs)
>>> url
'/kkm/'

That resolved the issue for me. However, I do not know if that would break something else.

Regards,
Hieu

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions