Suppose you've written an app, named it 'myapp', and want to make it the default, with its name always removed. Your default controller is still 'default', and you want to remove its name from user-visible URLs as well. Here's what you put in routes.py:
routers = dict(
BASE = dict( default_application='myapp' ),
)
That's it. And it's smart enough to know how to do the right thing with URLs like:
http://domain.com/myapp/default/myapp
or http://domain.com/myapp/myapp/index
...where normal shortening would be ambiguous.
If you have two applications, myapp and myapp2, you'll get the same effect, and additionally myapp2's default controller will be stripped from the URL whenever it's safe (which is mostly all the time).
Another case. Suppose you want to support URL-based languages, where your URLs look like this:
or (rewritten)
Here's how:
routers = dict(
BASE = dict( default_application='myapp' ),
myapp = dict( languages=['en', 'it', 'jp'], default_language='en' ),
)
Now an incoming URL like this:
http:/domain.com/it/some/path
will be routed to /myapp/some/path, and request.uri_language will be set to 'it', so you can force the translation. You can also have language-specific static files.
http://domain.com/it/static/filename
will be mapped to:
applications/myapp/static/it/filename
...if that file exists. If it doesn't, then URLs like:
http://domain.com/it/static/base.css
...will still map to:
applications/myapp/static/base.css
(because there is no static/it/base.css)
So you can now have language-specific static files, including images, if you need to.
Domain mapping is supported as well.
routers = dict(
BASE = dict(
domains = {
'domain1.com' : 'app1',
'domain2.com' : 'app2',
}
),
)
does what you'd expect.
routers = dict(
BASE = dict(
domains = {
'domain.com:80' : 'app/insecure',
'domain.com:443' : 'app/secure',
}
),
)
...maps http://domain.com accesses to app's controller named 'insecure', while https accesses go to the 'secure' controller. Or you can map different ports to different apps, in the obvious way.
There's more, but mostly everything happens automatically, and there's no need to dig into the details of configuration unless there's some non-standard thing you need. There's a bit more documentation in router.example.py.
Please do. Take a look at router.example.py as well; there's at least one new feature there (path_prefix). I can review the material any time you're ready.
On 17 February 2011 08:02, Jonathan Lundell <jlun...@pobox.com> wrote:Please do. Take a look at router.example.py as well; there's at least one new feature there (path_prefix). I can review the material any time you're ready.On Feb 16, 2011, at 9:49 PM, cjrh wrote:
>
> On Feb 16, 11:38 pm, Jonathan Lundell <jlund...@pobox.com> wrote:
>> [I'm reposting this message from a while back, because the new release 1.92.1 contains it for the first time. It describes some simple use cases for the new URL router. Note that there are still some things that you'll need the existing regex-based router for, but for most cases the new one should work and be easier to configure.]
>
> May I add this text to the book, now that the router is in the latest
> stable release version? I don't want to step on your toes, if you
> were planning to do this yourself soon anyway, then that's fine too.
I am afraid router.example.py did not help me a lot.
What is unclear to me is how to use root_static. The documetation says:
# root_static: list of static files accessed from root
# (mapped to the current application's static/ directory)
# Each application has its own root-static files.
The example did not show any reference to an application.
So do I have to list all the files all the applications will user from 'static' in the same list?
I have two applications and at the moment have the following:
# base router
BASE = dict(
default_application = 'kb',
domains = {
'kbase.sun.ac.za' : 'kb',
'kbase' : 'kb',
'sadcpublications.sun.ac.za' : 'sadec'
},
applications = ['kb', 'sadec'],
controllers = 'DEFAULT'
),
Both sadec and kb makes use of a front page image:
in sadec/static for sadec
and in
kb/static/images for kb
While sadec shows the image kb can't find it.
I have used autoroutes before but that breaks jqgrid. Now with this jqgrid seems to work.
If I can just solve the problem of <app>/static/images it will help - that is until I discover something else routes.py has broken...
No, this is a special case for favicon.ico and robots.txtBy "root_static" (which I agree is a little confusing), we mean static files like favicon.ico that are always accessed in the server root. This is simply a list of those files (and the default list should normally be all you need) that tells web2py which application's static directory to fetch the files from.
I have two applications and at the moment have the following:
# base router
BASE = dict(
default_application = 'kb',
domains = {
'kbase.sun.ac.za' : 'kb',
'kbase' : 'kb',
'sadcpublications.sun.ac.za' : 'sadec'
},
applications = ['kb', 'sadec'],
controllers = 'DEFAULT'
),
Both sadec and kb makes use of a front page image:
in sadec/static for sadec
and in
kb/static/images for kb
While sadec shows the image kb can't find it.
I have used autoroutes before but that breaks jqgrid. Now with this jqgrid seems to work.
If I can just solve the problem of <app>/static/images it will help - that is until I discover something else routes.py has broken...
What are the actual URLs for the images in question? As they appear in the page source at the browser, I mean.
src="/kb/static/DSC_1403.png" (I moved the image from /kb/static/images to /kb/static - now it works.
I would still like to know how to get imgages in static/images to work with this routing system.
src="/sadec/static/sadc.png"
Regards
Johann
It should "just work". Can you double-check your names?
Also, please try it (with files in static/images) with routing turned off (rename routes.py and restart web2py).
If that doesn't resolve things, I'll try a more extended test at my end.
I wrote a unit test, below, that seems to work OK. The extra kb & sadec router entries are there because the apps aren't in my file system; you don't need them.
def test_johann_spies(self):
'''
test Johann Spies router
'''
router_js = dict(
BASE = dict(
default_application = 'kb',
domains = {
'kbase.sun.ac.za' : 'kb',
'kbase' : 'kb',
'sadcpublications.sun.ac.za' : 'sadec'
},
applications = ['kb', 'sadec'],
),
kb = dict(),
sadec = dict(),
)
load(rdict=router_js)
self.assertEqual(filter_url('http://kbase.sun.ac.za/'),
'/kb/default/index')
self.assertEqual(filter_url('http://kbase/'),
'/kb/default/index')
self.assertEqual(filter_url('http://sadcpublications.sun.ac.za/'),
'/sadec/default/index')
self.assertEqual(filter_url('http://kbase.sun.ac.za/static/file.css'),
'%s/applications/kb/static/file.css' % root)
self.assertEqual(filter_url('http://kbase.sun.ac.za/static/file.css'),
'%s/applications/kb/static/file.css' % root)
self.assertEqual(filter_url('http://kbase.sun.ac.za/static/DSC_1403.png'),
'%s/applications/kb/static/DSC_1403.png' % root)
self.assertEqual(filter_url('http://kbase.sun.ac.za/static/images/DSC_1403.png'),
'%s/applications/kb/static/images/DSC_1403.png' % root)
self.assertEqual(filter_url('http://kbase/static/images/DSC_1403.png'),
'%s/applications/kb/static/images/DSC_1403.png' % root)
You choose one or the other, as routes.py.
Either can be used at the app level, though if you do so, you need a root routes.py as well.
With router.example.py, it's probably just as easy to put the app-specfic routing configuration(s) in the root router, though.
> 2) What would a routing file look like that matches the current default routing behavior? I would much prefer to have explicit routing. I don't like that routing is "off" be default.
"Routing" is perhaps a slightly misleading name. The default is to treat the first three elements of a URL as application/controller/function, to split the remaining path elements into request.args (a list), and to put the query string in request.vars (a dictionary).
If there are fewer than three elements in the URL, the URL is (effectively) filled out as required with /init/default/index.
The function of routes.py is to map the incoming URL to the above format, and vice versa for outgoing URLs.
> 3) I would think one of the most important and simplest use cases would be: hide the default application name, hide "default" and hide "index". This would be so that someone could role out one application on a server and have clean, user-friendly URLs.
Yes.
> 4) Does the new routing functionality support mixed regex and non-regex?
No, though it's on the list for future enhancement.
src="/kb/static/DSC_1403.png" (I moved the image from /kb/static/images to /kb/static - now it works.
I would still like to know how to get imgages in static/images to work with this routing system.
Just set default_application (in BASE) to "myapp". default_controller is set to "default" by default, and the rest happens automatically.
I made a change just before the 1.92.1 release. By default, static URLs are not shortened. In this case, URLs like /myapp/static/something don't have "myapp" hidden.
There are two reasons for this. One is that static URLs are typically invisible to users, so it doesn't matter. The more important reason is to make life easier for external routing of static URLs (by Apache or GAE, for example).
You can override this behavior, and get app-hiding back for static URLs, by setting map_static=True.
On Feb 17, 2011, at 10:37 AM, Johann Spies wrote:It should "just work". Can you double-check your names?
> What are the actual URLs for the images in question? As they appear in the page source at the browser, I mean.
>
>
> src="/kb/static/DSC_1403.png" (I moved the image from /kb/static/images to /kb/static - now it works.
>
> I would still like to know how to get imgages in static/images to work with this routing system.
>
>
> src="/sadec/static/sadc.png"
Also, please try it (with files in static/images) with routing turned off (rename routes.py and restart web2py).
If that doesn't resolve things, I'll try a more extended test at my end.
With routes.py in place, can you access something in static/images with their full (unshortened) URL?
And is this still the routers dict you're using? Anything besides BASE?
# base router
BASE = dict(
default_application = 'kb',
domains = {
'kbase.sun.ac.za' : 'kb',
'kbase' : 'kb',
'sadcpublications.sun.ac.za' : 'sadec'
},
applications = ['kb', 'sadec'],
controllers = 'DEFAULT'
),
src="/kb/static/DSC_1403.png" (I moved the image from /kb/static/images to /kb/static - now it works.
I would still like to know how to get imgages in static/images to work with this routing system.src="/sadec/static/sadc.png"
Is it possible that you were using URL calls like this:
URL(... c='static/images', f='filename') ?
If so, try changing them to this:
URL(... c='static', f='images/filename')
Do you have any URL calls with 'static/images' in them? (That is, in the non-working case.) Under some circumstances they won't work, in particular when there's a hyphen or underscore in the filename.
The fix (until the next version, anyway) is to change calls like URL('static/image', 'file') to URL('static', 'image/file'). The reason is that 'static' is treated as the name of the controller directory, and 'static/image' isn't recognized in the current version.
The fix (until the next version, anyway) is to change calls like URL('static/image', 'file') to URL('static', 'image/file'). The reason is that 'static' is treated as the name of the controller directory, and 'static/image' isn't recognized in the current version.
On 3 March 2011 08:39, Jonathan Lundell <jlun...@pobox.com> wrote:
The fix (until the next version, anyway) is to change calls like URL('static/image', 'file') to URL('static', 'image/file'). The reason is that 'static' is treated as the name of the controller directory, and 'static/image' isn't recognized in the current version.
Thanks. This fix worked for me.
I am designing an app with a URL structure like this:'yoursitename' is effectively an argument as there could be many sites and users are allowed to create their own sites. But I'd like it to be 'top level'.
What would be the mapping of, say, myapp.com/yoursitename/xyz? As a fully-expanded web2py URL? What are the app/controller/functions?
If I understand your requirement correctly, the new router logic will do what you want. You'll need to specify a functions list in order to enable removal of the default function from the URL. So the expanded URL would be:
myapp.com/yoursitename/users -> myapp.com/app/default/index/yoursitename/users, so you'd have request.args=['yoursitename', 'users'], and your default function would decide what to do with it.
/admin and the like could be functions (like /app/default/admin), in which case you'd want to say: functions=['admin', 'about', 'faq', 'accounts', 'etc'] in the router control dict, or they can be controllers.
I was playing with Flask and I have to say its solution to routing is very nice:The use of variable names anywhere within the URL structure is very handy. Anything like this possible in web2py?
sorry - should have been:@app.route('/<yoursitename>/users')... def editusers(yoursitename): pass
print url_for('editusers', yoursitename='Supersite')gives:/Supersite/users
$user/app/controller/etc...
It seems the domain is obviously the root of any app; then the "user"
selected name should be the next logical level; then any functions that
user has access to should be listed;
I think that the only way to do it right now is by using the regex router to rewrite the URL, as above ($user should end up in request.args). I don't think it'd be all that hard to manage. You'd want to establish some rule for recognizing the case where the first element isn't a $user (say on initial login).
This is not an option in web2py because the controller is executed
after the requests arrives not imported before (as in Flask). Anyway,
that works better for simple apps but becomes a mess if you have many
functions because the routing info is scattered all over the place.
Moreover - when it comes to rest - it requires that you use the same
function for multiple methods (GET/POST/etc) by using multiple
decorators or that you use different functions with different names to
handle different names (I do not like that).
Notice that the routing mechanism you're talking about doesn't depend on the routing information being distributed as controller decoration; they can be presented in a table instead. In fact, putting them in a table has advantages over the decorator approach, in that a) you can see the whole routing policy in one place, and b) you can explicitly specify the order of evaluation.
Checking controller arguments is important, since they are, after all, user input and not trustworthy. And there's something to be said for checking them at the controller/function, since you're in a position to give more informative error messages if something is wrong.
If you're using web2py forms, then the validators serve this purpose. If you're not, you can use the validators on their own.
Noticing the version of web2py keeps interestingly increasing to 2.0,
I think this is something Massimo might want to spend sometime
thinking about.
You can do this with the new router if you make post the default function of the blog app, and create a list of functions in blog/default. Something like this:
routers = dict(
blog = dict(
default_function='post',
functions=['index', 'user', 'post', 'whatever'],
),
)
You'll need to parse and validate the args in default/post.
By specifying a list of functions in the default controller, you're telling the router that it can unambiguously omit the 'post' name if there's no conflict. So with the router above, a URL like /blog/default/post/index would *not* be shortened to /blog/index, because that would conflict with the function named 'index'.
Yes but not in the application folder. The importance is to be distributed with the application.
I think you two might be talking at cross purposes here. It goes in applications/myapp/
>
> With both systesms, you can put a routes.py file in the application folder, but at least with the older system, for that to work, you also need a routes_app entry in the main routes.py file -- is that what you're saying you don't want? In other words, you want no dependency on any central routes.py file? Perhaps Jonathan can verify, but I think that may be possible with the new system (i.e., having a routes.py file in your app folder without having a routes.py file in the web2py root folder).
>
Both versions of routing work the same way, in principle. You need a base (root) routes.py so that the router can figure out the application (otherwise it wouldn't know which app-specific routes.py to use). The rest of the routing information can be in each applications root folder.
The new router's syntax makes it pretty easy to have all the routing in the base routes.py, though, since each app can simply have its own routing dict.
+1 for a move towards routes default "on" and primarily controlled at the app level. But I do recognize there is an issue about what file should come in the main distribution and how to avoid it being overwritten with updates.The default files could duplicate the current default functionality for explicitness.