librelist archives

« back to archive

Advice when routes overlap

Advice when routes overlap

From:
Desmond Rivet
Date:
2013-01-20 @ 00:40
Hi all,

I'm writing a blogging engine with flask.  My route to serve up a
particular blog entry looks like this:

/<path:category>/<slug>

Archives are supported in this blogging engine.  An example of such a
route looks like this:

/<path:category>/<int:year>/

When I dump the URL routing rules, I find that they are in this order:

/<path:category>/<slug>
/<path:category>/<int:year>/

which means that when I access a URL like this:

/cooking/2007

It get matched to the first route (/<path:category>/<slug>) and not
the second route.  This is not the behaviour I'd like.  I'd like it to
match against the archive route.

If I access this URL:

/cooking/2007/

Then we get the correct route (/<path:category>/<int:year>/).  I think
that if the archive route were somehow ordered before the "slug"
route, then we'd have the behaviour I wanted, because flask will tack
on a trailing slash if it finds that one is lacking.

Now, I know that my routes are naturally ambiguous.  I understand that
"/<path:category>/<slug>" and "/<path:category>/<int:year>/" overlap.
And I know that I could likely resolve any problems by picking a less
ambiguous URL scheme.  I could, for example, use a keyword like
"archive" to specify that we have an archive URL, like this:

/archives/<path:category>/<int:year>/

But...I would prefer not to have to do that.  I like the idea of my
"year" overriding my "category" (i.e. I'm okay with categories not
having any numbers in them).  What I want, basically, is for the
archive routes to override all other routes.

This is difficult to accomplish with Flask, from what I can tell.
There doesn't seem to be a way to force a particular ordering to the
routes, or to specify that the route should be checked in a particular
order.  They are ordered by "complexity", though I'm not sure how
that's measured.

Does anyone have any advice?  Or am I just doing this "wrong"?

Thanks for any help you can give me on this topic.

Desmond

Re: [flask] Advice when routes overlap

From:
Jesaja Everling
Date:
2013-01-20 @ 01:06
Hi Desmond,

maybe using a custom converter that matches only slugs that for example 
don't begin with a number would work?


http://stackoverflow.com/questions/5870188/does-flask-support-regular-expressions-in-its-url-routing


Best Regards,

Jesaja Everling


On Sun Jan 20 2013 01:40:25 AM CET, Desmond Rivet <desmond.rivet@gmail.com> wrote:

> Hi all,
> 
> I'm writing a blogging engine with flask.   My route to serve up a
> particular blog entry looks like this:
> 
> /<path:category>/<slug>
> 
> Archives are supported in this blogging engine.   An example of such a
> route looks like this:
> 
> /<path:category>/<int:year>/
> 
> When I dump the URL routing rules, I find that they are in this order:
> 
> /<path:category>/<slug>
> /<path:category>/<int:year>/
> 
> which means that when I access a URL like this:
> 
> /cooking/2007
> 
> It get matched to the first route (/<path:category>/<slug>) and not
> the second route.   This is not the behaviour I'd like.   I'd like it to
> match against the archive route.
> 
> If I access this URL:
> 
> /cooking/2007/
> 
> Then we get the correct route (/<path:category>/<int:year>/).   I think
> that if the archive route were somehow ordered before the "slug"
> route, then we'd have the behaviour I wanted, because flask will tack
> on a trailing slash if it finds that one is lacking.
> 
> Now, I know that my routes are naturally ambiguous.   I understand that
> "/<path:category>/<slug>" and "/<path:category>/<int:year>/" overlap.
> And I know that I could likely resolve any problems by picking a less
> ambiguous URL scheme.   I could, for example, use a keyword like
> "archive" to specify that we have an archive URL, like this:
> 
> /archives/<path:category>/<int:year>/
> 
> But...I would prefer not to have to do that.   I like the idea of my
> "year" overriding my "category" (i.e. I'm okay with categories not
> having any numbers in them).   What I want, basically, is for the
> archive routes to override all other routes.
> 
> This is difficult to accomplish with Flask, from what I can tell.
> There doesn't seem to be a way to force a particular ordering to the
> routes, or to specify that the route should be checked in a particular
> order.   They are ordered by "complexity", though I'm not sure how
> that's measured.
> 
> Does anyone have any advice?   Or am I just doing this "wrong"?
> 
> Thanks for any help you can give me on this topic.
> 
> Desmond

Re: [flask] Advice when routes overlap

From:
Desmond Rivet
Date:
2013-01-20 @ 06:07
This is an interesting idea...

On Sat, Jan 19, 2013 at 8:06 PM, Jesaja Everling <jeverling@gmail.com> wrote:
> Hi Desmond,
>
> maybe using a custom converter that matches only slugs that for example 
don't begin with a number would work?
>
> 
http://stackoverflow.com/questions/5870188/does-flask-support-regular-expressions-in-its-url-routing
>
> Best Regards,
>
> Jesaja Everling
>
>
> On Sun Jan 20 2013 01:40:25 AM CET, Desmond Rivet 
<desmond.rivet@gmail.com> wrote:
>
>> Hi all,
>>
>> I'm writing a blogging engine with flask.   My route to serve up a
>> particular blog entry looks like this:
>>
>> /<path:category>/<slug>
>>
>> Archives are supported in this blogging engine.   An example of such a
>> route looks like this:
>>
>> /<path:category>/<int:year>/
>>
>> When I dump the URL routing rules, I find that they are in this order:
>>
>> /<path:category>/<slug>
>> /<path:category>/<int:year>/
>>
>> which means that when I access a URL like this:
>>
>> /cooking/2007
>>
>> It get matched to the first route (/<path:category>/<slug>) and not
>> the second route.   This is not the behaviour I'd like.   I'd like it to
>> match against the archive route.
>>
>> If I access this URL:
>>
>> /cooking/2007/
>>
>> Then we get the correct route (/<path:category>/<int:year>/).   I think
>> that if the archive route were somehow ordered before the "slug"
>> route, then we'd have the behaviour I wanted, because flask will tack
>> on a trailing slash if it finds that one is lacking.
>>
>> Now, I know that my routes are naturally ambiguous.   I understand that
>> "/<path:category>/<slug>" and "/<path:category>/<int:year>/" overlap.
>> And I know that I could likely resolve any problems by picking a less
>> ambiguous URL scheme.   I could, for example, use a keyword like
>> "archive" to specify that we have an archive URL, like this:
>>
>> /archives/<path:category>/<int:year>/
>>
>> But...I would prefer not to have to do that.   I like the idea of my
>> "year" overriding my "category" (i.e. I'm okay with categories not
>> having any numbers in them).   What I want, basically, is for the
>> archive routes to override all other routes.
>>
>> This is difficult to accomplish with Flask, from what I can tell.
>> There doesn't seem to be a way to force a particular ordering to the
>> routes, or to specify that the route should be checked in a particular
>> order.   They are ordered by "complexity", though I'm not sure how
>> that's measured.
>>
>> Does anyone have any advice?   Or am I just doing this "wrong"?
>>
>> Thanks for any help you can give me on this topic.
>>
>> Desmond
>

Re: [flask] Advice when routes overlap

From:
Steven Kryskalla
Date:
2013-01-20 @ 00:59
On Sat, Jan 19, 2013 at 4:40 PM, Desmond Rivet <desmond.rivet@gmail.com> wrote:
> which means that when I access a URL like this:
>
> /cooking/2007
>
> It get matched to the first route (/<path:category>/<slug>) and not
> the second route.  This is not the behaviour I'd like.  I'd like it to
> match against the archive route.
>
> If I access this URL:
>
> /cooking/2007/
>
> Then we get the correct route (/<path:category>/<int:year>/).

Can you show your code? I don't get the same result:

$ curl -L http://127.0.0.1:5000/cooking/2007
show ARCHIVE for year 2007 and category u'cooking'

$ curl http://127.0.0.1:5000/cooking/2007/
show ARCHIVE for year 2007 and category u'cooking'

$ curl http://127.0.0.1:5000/cooking/pizza
show POST titled u'pizza' for category u'cooking'

Here are the views I used:

@app.route("/<path:category>/<int:year>/")
def archive(category, year):
    return "show ARCHIVE for year %r and category %r" % (year, category)

@app.route("/<path:category>/<slug>")
def post(category, slug):
    return "show POST titled %r for category %r" % (slug, category)

-steve

Re: [flask] Advice when routes overlap

From:
Jesaja Everling
Date:
2013-01-20 @ 01:09
Yes, of course, just changing the order will work. The int converter will 
not match slugs, so there is no reason to use custom converters if you 
just define the catch-all route last.

Best Regards,

Jesaja Everling


On Sun Jan 20 2013 01:59:13 AM CET, Steven Kryskalla <skryskalla@gmail.com> wrote:

> On Sat, Jan 19, 2013 at 4:40 PM, Desmond Rivet <desmond.rivet@gmail.com>
> wrote:
> > which means that when I access a URL like this:
> > 
> > /cooking/2007
> > 
> > It get matched to the first route (/<path:category>/<slug>) and not
> > the second route.   This is not the behaviour I'd like.   I'd like it to
> > match against the archive route.
> > 
> > If I access this URL:
> > 
> > /cooking/2007/
> > 
> > Then we get the correct route (/<path:category>/<int:year>/).
> 
> Can you show your code? I don't get the same result:
> 
> $ curl -L http://127.0.0.1:5000/cooking/2007
> show ARCHIVE for year 2007 and category u'cooking'
> 
> $ curl http://127.0.0.1:5000/cooking/2007/
> show ARCHIVE for year 2007 and category u'cooking'
> 
> $ curl http://127.0.0.1:5000/cooking/pizza
> show POST titled u'pizza' for category u'cooking'
> 
> Here are the views I used:
> 
> @app.route("/<path:category>/<int:year>/")
> def archive(category, year):
>         return "show ARCHIVE for year %r and category %r" % (year, category)
> 
> @app.route("/<path:category>/<slug>")
> def post(category, slug):
>         return "show POST titled %r for category %r" % (slug, category)
> 
> -steve

Re: [flask] Advice when routes overlap

From:
Desmond Rivet
Date:
2013-01-20 @ 01:46
I was under the impression that the order in which you call
add_url_rule() or route() didn't matter - internally, Flask would just
order your routes using its own "complexity" sort order.  Is that
wrong?

On Sat, Jan 19, 2013 at 8:09 PM, Jesaja Everling <jeverling@gmail.com> wrote:
> Yes, of course, just changing the order will work. The int converter 
will not match slugs, so there is no reason to use custom converters if 
you just define the catch-all route last.
>
> Best Regards,
>
> Jesaja Everling
>
>
> On Sun Jan 20 2013 01:59:13 AM CET, Steven Kryskalla 
<skryskalla@gmail.com> wrote:
>
>> On Sat, Jan 19, 2013 at 4:40 PM, Desmond Rivet <desmond.rivet@gmail.com>
>> wrote:
>> > which means that when I access a URL like this:
>> >
>> > /cooking/2007
>> >
>> > It get matched to the first route (/<path:category>/<slug>) and not
>> > the second route.   This is not the behaviour I'd like.   I'd like it to
>> > match against the archive route.
>> >
>> > If I access this URL:
>> >
>> > /cooking/2007/
>> >
>> > Then we get the correct route (/<path:category>/<int:year>/).
>>
>> Can you show your code? I don't get the same result:
>>
>> $ curl -L http://127.0.0.1:5000/cooking/2007
>> show ARCHIVE for year 2007 and category u'cooking'
>>
>> $ curl http://127.0.0.1:5000/cooking/2007/
>> show ARCHIVE for year 2007 and category u'cooking'
>>
>> $ curl http://127.0.0.1:5000/cooking/pizza
>> show POST titled u'pizza' for category u'cooking'
>>
>> Here are the views I used:
>>
>> @app.route("/<path:category>/<int:year>/")
>> def archive(category, year):
>>         return "show ARCHIVE for year %r and category %r" % (year, category)
>>
>> @app.route("/<path:category>/<slug>")
>> def post(category, slug):
>>         return "show POST titled %r for category %r" % (slug, category)
>>
>> -steve
>

Re: [flask] Advice when routes overlap

From:
Steven Kryskalla
Date:
2013-01-20 @ 05:06
On Sat, Jan 19, 2013 at 5:46 PM, Desmond Rivet <desmond.rivet@gmail.com> wrote:
> I was under the impression that the order in which you call
> add_url_rule() or route() didn't matter - internally, Flask would just
> order your routes using its own "complexity" sort order.  Is that
> wrong?
>

Yea, rules are sorted by how complex they are:

https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/routing.py#L771-L793

I was also using flask 0.9 and werkzeug 0.8.3 in my simpler example
where they were ordered correctly. Your code looks OK, but without
being able to run it I'm not sure where the problem is. Also, it might
make a difference at what point register_blueprint gets called (didn't
see it the code you posted). Lastly, I don't know if this is related,
but when you printed the rules, the rule you're having trouble with is
misspelled "/<path:categodry>/<int:year>/".

I would try to reproduce the problem with a smaller piece of code to
make it easier to find the problem.

-Steve

Re: [flask] Advice when routes overlap

From:
Desmond Rivet
Date:
2013-01-27 @ 02:46
Hi again,

So I have a few things to report on this issue.

I was actually running a patched version of werkzeug to fix a static
file issue (more on this later).  When I revert the patch and run my
routes, then the <year> route takes precedence over the <slug> route.
I have a one file program with no blueprints to show the issue:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def url01():
    return 'root'

@app.route('/<path:path>')
def url02(path):
    return 'path: %s' % path

@app.route('/<path:path>/<slug>.<flav>')
def url02_01(path, slug, flav):
    return 'path: %s, slug: %s, flav: %s' % (path,slug,flav)

@app.route('/<path:path>/<slug>')
def url02_02(path, slug):
    return 'path: %s, slug: %s' % (path,slug)

@app.route('/<slug>.<flav>')
def url02_03(slug,flav):
    return 'slug: %s, flav: %s' % (slug,flav)

@app.route('/<slug>')
def url02_04(slug):
    return 'slug: %s' % slug

# tags
@app.route('/<path:path>/tags/<tag>/')
@app.route('/<path:path>/tags/<tag>/index')
def url03(path, tag):
    return 'path: %s, tag: %s' % (path, tag)

@app.route('/<path:path>/tags/<tag>/index.<flav>')
def url04(path, tag, flav):
    return 'path: %s, tag: %s, flavour: %s' % (path, tag, flav)

@app.route('/tags/<tag>/')
@app.route('/tags/<tag>/index')
def url05(tag):
    return 'tag: %s' % tag

@app.route('/tags/<tag>/index.<flav>')
def url06(tag, flav):
    return 'tag: %s, flavour: %s' % (tag, flav)

#search
@app.route('/<path:path>/search/')
@app.route('/<path:path>/search/index')
def url07(path):
    return 'search path: %s' % path

@app.route('/<path:path>/search/index.<flav>')
def url08(path, flav):
    return 'search path: %s, flavour' % (path, flav)

@app.route('/search/')
@app.route('/search/index')
def url07():
    return 'search'

@app.route('/search/index.<flav>')
def url08(flav):
    return 'search % flav' % flav

# permalink
@app.route('/<path:path>/<int:year>/<int:month>/<int:day>/<slug>.<flav>')
def url09(path, year, month, day, slag, flav):
    return 'permalink: path %s, year %s, month %s, day %s, slug %s,
flav %s' % (path, year, month, day, slug, flav)

@app.route('/<path:path>/<int:year>/<int:month>/<int:day>/<slug>')
def url10(path, year, month, day, slug):
    return 'permalink: path %s, year %s, month %s, day %s, slug %s' %
(path, year, month, day, slug)

@app.route('/<int:year>/<int:month>/<int:day>/<slug>.<flav>')
def url11(year, month, day, slug, flav):
    return 'permalink: year %s, month %s, day %s, slug %s, flav %s' %
(year, month, day, slug, flav)

@app.route('/<int:year>/<int:month>/<int:day>/<slug>')
def url12(year, month, day, slug):
    return 'permalink: year %s, month %s, day %s, slug %s' % (year,
month, day, slug)

# archives
@app.route('/<path:path>/<int:year>/')
@app.route('/<path:path>/<int:year>/index')
def url13(path,year):
    return 'path: %s, year: %s' % (path, year)

@app.route('/<path:path>/<int:year>/<int(fixed_digits=2):month>/')
@app.route('/<path:path>/<int:year>/<int(fixed_digits=2):month>/index')
def url14(path,year,month):
    return 'path: %s, year: %s, month: %s' % (path, year, month)


@app.route('/<path:path>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/')

@app.route('/<path:path>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index')
def url15(path,year,month,day):
    return 'path: %s, year: %s, month: %s, day: %s' % (path, year, month, day)

@app.route('/<path:path>/<int:year>/index.<flav>')
def url16(path,year,flav):
    return 'path: %s, year: %s, flav %s' % (path, year, flav)

@app.route('/<path:path>/<int:year>/<int(fixed_digits=2):month>/index.<flav>')
def url17(path,year,month,flav):
    return 'path: %s, year: %s, month: %s, flav %s' % (path, year, month, flav)


@app.route('/<path:path>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index.<flav>')
def url18(path,year,month,day,flav):
    return 'path: %s, year: %s, month: %s, day: %s, flav %s' % (path,
year, month, day, flav)

@app.route('/<int:year>/')
@app.route('/<int:year>/index')
def url19(year):
    return 'year: %s' % (year)

@app.route('/<int:year>/<int(fixed_digits=2):month>/')
@app.route('/<int:year>/<int(fixed_digits=2):month>/index')
def url20(year,month):
    return 'year: %s, month: %s' % (year, month)

@app.route('/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/')

@app.route('/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index')
def url21(year,month,day):
    return 'year: %s, month: %s, day: %s' % (year, month, day)

@app.route('/<int:year>/index.<flav>')
def url22(year,flav):
    return 'year: %s, flav %s' % (year, flav)

@app.route('/<int:year>/<int(fixed_digits=2):month>/index.<flav>')
def url23(year,month,flav):
    return 'year: %s, month: %s, flav %s' % (year, month, flav)


@app.route('/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index.<flav>')
def url24(year,month,day,flav):
    return 'year: %s, month: %s, day: %s, flav %s' % (year, month, day, flav)

for rule in app.url_map.iter_rules():
    print rule

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

If you run this program, the rules get printed out and you see this:

/search/index
/search/
/
/<path:path>/<int:year>/<int:month>/<int:day>/<slug>.<flav>
/<int:year>/<int:month>/<int:day>/<slug>.<flav>

/<path:path>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index.<flav>
/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index.<flav>
/<path:path>/tags/<tag>/index.<flav>
/<path:path>/<int:year>/<int(fixed_digits=2):month>/index.<flav>

/<path:path>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index
/<path:path>/<int:year>/<int:month>/<int:day>/<slug>
/tags/<tag>/index.<flav>
/<int:year>/<int(fixed_digits=2):month>/index.<flav>
/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index
/<int:year>/<int:month>/<int:day>/<slug>
/<path:path>/search/index.<flav>
/<path:path>/tags/<tag>/index
/<path:path>/<int:year>/index.<flav>
/<path:path>/<int:year>/<int(fixed_digits=2):month>/index
/<path:path>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/
/<path:path>/<slug>.<flav>
/search/index.<flav>
/tags/<tag>/index
/<int:year>/index.<flav>
/<int:year>/<int(fixed_digits=2):month>/index
/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/
/<slug>.<flav>
/<path:path>/search/index
/<path:path>/tags/<tag>/
/<path:path>/<int:year>/index
/<path:path>/<int:year>/<int(fixed_digits=2):month>/
/static/<path:filename>
/tags/<tag>/
/<int:year>/index
/<int:year>/<int(fixed_digits=2):month>/
/<path:path>/search/
/<path:path>/<int:year>/
/<path:path>/<slug>
/<int:year>/
/<slug>
/<path:path>

Note two things:

1. The "/<path:path>/<int:year>/" route comes before the
"/<path:path>/<slug>", as I wanted, which is good, BUT

2. The "/<path:path>/<slug>.<flav>" route comes before the
"/static/<path:filename>", thus blocking files like
"/static/site.css", which is why I applied the aforementioned patch in
the first place.

The "static routing bug" patch was taken from here:

https://github.com/mitsuhiko/werkzeug/pull/196

So, I fix one problem and get another...

On Sun, Jan 20, 2013 at 12:06 AM, Steven Kryskalla <skryskalla@gmail.com> wrote:
> On Sat, Jan 19, 2013 at 5:46 PM, Desmond Rivet <desmond.rivet@gmail.com> wrote:
>> I was under the impression that the order in which you call
>> add_url_rule() or route() didn't matter - internally, Flask would just
>> order your routes using its own "complexity" sort order.  Is that
>> wrong?
>>
>
> Yea, rules are sorted by how complex they are:
>
> https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/routing.py#L771-L793
>
> I was also using flask 0.9 and werkzeug 0.8.3 in my simpler example
> where they were ordered correctly. Your code looks OK, but without
> being able to run it I'm not sure where the problem is. Also, it might
> make a difference at what point register_blueprint gets called (didn't
> see it the code you posted). Lastly, I don't know if this is related,
> but when you printed the rules, the rule you're having trouble with is
> misspelled "/<path:categodry>/<int:year>/".
>
> I would try to reproduce the problem with a smaller piece of code to
> make it easier to find the problem.
>
> -Steve

Re: [flask] Advice when routes overlap

From:
Desmond Rivet
Date:
2013-01-20 @ 06:06
On Sun, Jan 20, 2013 at 12:06 AM, Steven Kryskalla <skryskalla@gmail.com> wrote:
> On Sat, Jan 19, 2013 at 5:46 PM, Desmond Rivet <desmond.rivet@gmail.com> wrote:
>> I was under the impression that the order in which you call
>> add_url_rule() or route() didn't matter - internally, Flask would just
>> order your routes using its own "complexity" sort order.  Is that
>> wrong?
>>
>
> Yea, rules are sorted by how complex they are:
>
> https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/routing.py#L771-L793
>
> I was also using flask 0.9 and werkzeug 0.8.3 in my simpler example
> where they were ordered correctly. Your code looks OK, but without

Okay.  Note that I was also having problems with my static files being
overshadowed by some of my rules as well.  I found a patch somewhere
that seemed to fix the problem, but it hasn't yet made it into the
main source code.

> being able to run it I'm not sure where the problem is. Also, it might
> make a difference at what point register_blueprint gets called (didn't

register_blueprint is called after all the routes are defined:

app.category_bp = category

// more route definitions spread over different files, in different modules

app.register_blueprint(category, url_prefix='/<path:category>')
app.register_blueprint(category, url_prefix='')

> see it the code you posted). Lastly, I don't know if this is related,
> but when you printed the rules, the rule you're having trouble with is
> misspelled "/<path:categodry>/<int:year>/".

No, it's not misspelled in my print out.  Something went awry in the
pasting to the email.  But good catch!  I didn't even notice that.

Desmond

Re: [flask] Advice when routes overlap

From:
Desmond Rivet
Date:
2013-01-20 @ 01:32
Hi,

Thanks for the reply.  First off I should point out that I'm using
Flask 0.9, Werkzeug 0.8.3, and Jinga2 2.6.

The code is spread out over many files.  But the routing in question
looks like this.

Initially we run this:

    category = Blueprint('category', __name__)

    @category.url_value_preprocessor
    def pull_category(endpoint, values):
        g.category = ''
        if 'category' in values:
            g.category = values.pop('category')

    @category.url_defaults
    def add_category(endpoint, values):
        # url_for isn't always called in a request context, it's also
        # called during a "walk" in Flask-Script.  So we can't assume
        # that there's a g.category defined
        if 'category' in values or not g.category:
            return
        if app.url_map.is_endpoint_expecting(endpoint, 'category'):
            values['category'] = g.category

    @category.route('/')
    def home():
        return _handle_category_url(None, g.category)

    @category.route('/<slug>')
    def post(slug):
        return _handle_categorized_url(None, g.category, slug)

    @category.route('/<slug>.<flav>')
    def post_flav(slug, flav):
        return _handle_categorized_url(flav, g.category, slug)

    app.category_bp = category

And then we run this in another python file (archiving.py):

        # Permalinks
        
app.category_bp.add_url_rule('/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/<slug>',
                                     view_func =
self._pl_view_func('permalink'))

        
app.category_bp.add_url_rule('/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/<slug>.<flav>',
                                     view_func =
self._pl_view_func('permalink_flav'))

        # Date URLs
        app.category_bp.add_url_rule('/<int:year>/', view_func =
self._view_func('archive'))
        app.category_bp.add_url_rule('/<int:year>/<int(fixed_digits=2):month>/',
                                     view_func = self._view_func('archive'))
        
app.category_bp.add_url_rule('/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/',
                                     view_func = self._view_func('archive'))

        app.category_bp.add_url_rule('/<int:year>/index',
                                     view_func =
self._view_func('archive_index'))
        
app.category_bp.add_url_rule('/<int:year>/<int(fixed_digits=2):month>/index',
                                     view_func =
self._view_func('archive_index'))
        
app.category_bp.add_url_rule('/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index',
                                     view_func =
self._view_func('archive_index'))

        app.category_bp.add_url_rule('/<int:year>/index.<flav>',
                                     view_func =
self._view_func('archive_index_flav'))
        
app.category_bp.add_url_rule('/<int:year>/<int(fixed_digits=2):month>/index.<flav>',
                                     view_func =
self._view_func('archive_index_flav'))
        
app.category_bp.add_url_rule('/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index.<flav>',
                                     view_func =
self._view_func('archive_index_flav'))

There are other files (plugins) that add more routes.  For example, I
have a tagging.py plugin :

        app.category_bp.add_url_rule('/tags/<tag>/',
                                     view_func =
self._view_func('tag_canonical'))
        app.category_bp.add_url_rule('/tags/<tag>/index',
                                     view_func = self._view_func('tag_index'))
        app.category_bp.add_url_rule('/tags/<tag>/index.<flav>',
                                     view_func =
self._view_func('tag_index_flav'))

When I print out the routes using this bit of code:

   for rule in app.url_map.iter_rules():
        print rule

I get this:

/
/tags/<tag>/index
/tags/<tag>/index.<flav>
/<path:category>/tags/<tag>/index.<flav>
/<path:category>/tags/<tag>/index
/search/
/search/index
/search/index.<flav>
/tags/<tag>/
/static/<path:filename>
/<slug>.<flav>
/<int:year>/index.<flav>
/<int:year>/index
/<path:category>/search/index.<flav>
/<path:category>/tags/<tag>/
/<path:category>/search/
/<path:category>/search/index
/<int:year>/<int(fixed_digits=2):month>/index.<flav>
/<path:category>/<slug>.<flav>
/<path:category>/<int:year>/index.<flav>
/<int:year>/<int(fixed_digits=2):month>/index
/<path:category>/<int:year>/index
/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index.<flav>
/<path:category>/<int:year>/<int(fixed_digits=2):month>/index.<flav>
/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index
/<path:category>/<int:year>/<int(fixed_digits=2):month>/index
/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/<slug>.<flav>

/<path:category>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index.<flav>

/<path:category>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/<slug>.<flav>

/<path:category>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/index
/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/<slug>

/<path:category>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/<slug>
/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/
/<path:category>/<int:year>/<int(fixed_digits=2):month>/<int(fixed_digits=2):day>/
/<int:year>/<int(fixed_digits=2):month>/
/<path:category>/<int:year>/<int(fixed_digits=2):month>/
/<slug>
/<int:year>/
/<path:category>/<slug>
/<path:categodry>/<int:year>/
/<path:category>/

You can see near the end:

/<path:category>/<slug>
/<path:category>/<int:year>/

On Sat, Jan 19, 2013 at 7:59 PM, Steven Kryskalla <skryskalla@gmail.com> wrote:
> On Sat, Jan 19, 2013 at 4:40 PM, Desmond Rivet <desmond.rivet@gmail.com> wrote:
>> which means that when I access a URL like this:
>>
>> /cooking/2007
>>
>> It get matched to the first route (/<path:category>/<slug>) and not
>> the second route.  This is not the behaviour I'd like.  I'd like it to
>> match against the archive route.
>>
>> If I access this URL:
>>
>> /cooking/2007/
>>
>> Then we get the correct route (/<path:category>/<int:year>/).
>
> Can you show your code? I don't get the same result:
>
> $ curl -L http://127.0.0.1:5000/cooking/2007
> show ARCHIVE for year 2007 and category u'cooking'
>
> $ curl http://127.0.0.1:5000/cooking/2007/
> show ARCHIVE for year 2007 and category u'cooking'
>
> $ curl http://127.0.0.1:5000/cooking/pizza
> show POST titled u'pizza' for category u'cooking'
>
> Here are the views I used:
>
> @app.route("/<path:category>/<int:year>/")
> def archive(category, year):
>     return "show ARCHIVE for year %r and category %r" % (year, category)
>
> @app.route("/<path:category>/<slug>")
> def post(category, slug):
>     return "show POST titled %r for category %r" % (slug, category)
>
> -steve