URL Generation#

The url_for utility provides a flexible way to generate URLs in your CherryPy application.
It handles path construction, query parameters, and supports multiple URL formats (absolute, relative, server-relative).

Setup#

1. Enable url_for in Jinja2 templates#

import cherrypy
from cherrypy_foundation.url import url_for

# Create Jinja2 environment with url_for available
env = cherrypy.tools.jinja2.create_env(
    package_name=__package__,
    globals={'url_for': url_for},
)

@cherrypy.tools.jinja2(env=env)
class Root:

    @cherrypy.expose
    @cherrypy.tools.jinja2(template='index.html')
    def index(self, **kwargs):
        return {}

Usage#

Simple path construction#

from cherrypy_foundation import url_for

# Simple path
url_for('/users')
# => '/users'

# Path with segments
url_for('/users', 123, 'edit')
# => '/users/123/edit'

# Path with query parameters
url_for('/search', q='python', page=2)
# => '/search?q=python&page=2'

Path Construction#

The function accepts multiple positional arguments that are joined to form the path:

Simple strings:

# String segments (leading/trailing slashes are handled automatically)
url_for('users', 'profile')
# => '/users/profile'

url_for('/users/', '/profile/')
# => '/users/profile'

Integers and basic objects get cast into strings:

# Integer segments
user_id = 42
url_for('/users', user_id)
# => '/users/42'

# Mixed types
url_for('/api', 'v1', 'users', 123, 'posts')
# => '/api/v1/users/123/posts'

Customize object URL representation with __url_for__() method:

# Objects with __url_for__() method
class User:
    def __init__(self, id):
        self.id = id

    def __url_for__(self):
        return f'users/{self.id}'

user = User(123)
url_for(user, 'edit')
# => '/users/123/edit'

Or define a url_for property:

# Objects with url_for attribute
class Article:
    def __init__(self, slug):
        self.url_for = f'articles/{slug}'

article = Article('hello-world')
url_for(article)
# => '/articles/hello-world'

url_for(article, 'comments')
# => '/articles/hello-world/comments'

URL Formats#

Use the _relative parameter to control the URL format:

# Default: CherryPy default behavior (usually server-relative)
url_for('/users')
# => '/users'

# Absolute URL (includes scheme and host)
url_for('/users', _relative=False)
# => 'https://example.com/users'

# Relative to current path
url_for('..', 'admin', _relative=True)
# => '../admin'

# Server-relative (starts with '/')
url_for('/api', 'users', _relative='server')
# => '/api/users'

Query Parameters#

Pass query parameters as keyword arguments:

url_for('/search', q='cherrypy', category='web', page=1)
# => '/search?category=web&page=1&q=cherrypy'

# None values are automatically filtered out
url_for('/search', q='test', filter=None)
# => '/search?q=test'

Special Behavior: Empty Path#

When no path arguments are provided, url_for preserves the current request’s path and merges query parameters:

# Current URL: /search?q=python&page=1

# Update only the page parameter
url_for(page=2)
# => '/search?page=2&q=python'

# Add a new parameter
url_for(sort='date')
# => '/search?page=1&q=python&sort=date'

Base URL Override#

Use _base to specify a custom base URL:

url_for('/api', 'users', _base='https://api.example.com')
# => 'https://api.example.com/api/users'

⚠️ Note: When called outside a request context, url_for will use the tools.proxy.base configuration if available.

In Jinja2 Templates#

The url_for function is not automatically available in Jinja2 templates. You need to make it available using:

import cherrypy_foundation.tools.jinja2

env = cherrypy.tools.jinja2.create_env(
    package_name=__package__,
    globals={
        'url_for': url_for,
    },
)

In you Jinja template:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Navigation</title>
  </head>
  <body>
    <nav>
      <a href="{{ url_for('/') }}">Home</a>
      <a href="{{ url_for('/users') }}">Users</a>
      <a href="{{ url_for('/users', user.id) }}">Profile</a>
      <a href="{{ url_for('/search', q='python') }}">Search</a>
    </nav>
  </body>
</html>

Complete Example#

import cherrypy
from cherrypy_foundation import url_for

class Root:
    
    @cherrypy.expose
    def index(self):
        # Redirect to user profile
        user_id = 123
        raise cherrypy.HTTPRedirect(url_for('/users', user_id))
    
    @cherrypy.expose
    @cherrypy.tools.jinja2(template='users.html')
    def users(self, user_id):
        # Generate URLs for template
        edit_url = url_for('/users', user_id, 'edit')
        delete_url = url_for('/users', user_id, 'delete')
        return {
            'edit_url': edit_url,
            'delete_url': delete_url
        }