Database Integration
Unlike frameworks such as Django, Tornado does not include a built-in Object-Relational Mappping (ORM) system. Developers must configure external libraries to manage database interactions. For MySQL environments, the torndb package is commonly used. Note that starting from Tornado version 3.0, the built-in database module was removed in favor of this standalone package. It acts as a wrapper around MySQLdb and supports Python 2, though community forks exist for Python 3 compatibility.
Setup and Initialization
Install the dependency:
pip install torndb
A database connection should be established once during application startup so it can be shared across request handlers. This instance is typically attached to the Application object.
import torndb
class MainApplication(tornado.web.Application):
def __init__(self):
routes = [
(r"/", HomeHandler),
]
settings = {
"template_path": "./templates",
"static_path": "./statics",
"debug": True
}
super().__init__(routes, **settings)
self.db_pool = torndb.Connection(
host="localhost",
database="inventory_db",
user="admin_user",
password="secure_pass"
)
Creating Schema
Prepare the table structure using standard SQL commands:
CREATE DATABASE inventory_db CHARACTER SET utf8;
USE inventory_db;
CREATE TABLE products (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
item_name VARCHAR(100) NOT NULL DEFAULT '',
location_code VARCHAR(50) NOT NULL DEFAULT '',
cost DECIMAL(10, 2) NOT NULL DEFAULT 0.0,
stock_count INT NOT NULL DEFAULT 10,
reviews INT NOT NULL DEFAULT 0,
PRIMARY KEY(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Execution Methods
For non-query operations like inserts or updates, use execute. This returns the ID of the last inserted row.
class AddProductHandler(RequestHandler):
def post(self):
name = self.get_argument("name")
loc = self.get_argument("loc")
price = float(self.get_argument("price"))
try:
new_id = self.application.db_pool.execute(
"INSERT INTO products(item_name, location_code, cost) VALUES(%s, %s, %s)",
name, loc, price
)
except Exception as error:
self.write(f"Error: {error}")
else:
self.write(f"Inserted ID: {new_id}")
To retrieve single records, use get, which returns a Row-like dictionary or None. Use query for multiple results returning a list of Rows.
class ViewProductHandler(RequestHandler):
def get(self):
pid = self.get_argument("id")
try:
record = self.application.db_pool.get(
"SELECT * FROM products WHERE id=%s", pid
)
if record:
print(record.item_name)
print(record["item_name"])
self.render("display.html", item=record)
else:
self.set_status(404)
self.write("Not found")
except Exception as e:
self.write(f"Query failed: {e}")
Security Mechanisms
Cookie Handling
Tornado extends standard HTTP cookies by providing methods to manage session data on the client side.
Basic Operations
Standard cookies can be set with optional expiration paths:
class CookieDemoHandler(RequestHandler):
def get(self):
# Set a simple cookie
self.set_cookie("session_token", "abc123")
# Set an expired cookie relative to days
self.set_cookie("pref_theme", "dark", expires_days=7)
# Read value back
token = self.get_cookie("session_token")
self.write(f"Token retrieved: {token}")
Clearing a cookie involves setting its value to empty and adjusting the expiration date immediately to the past. The browser then removes it.
def clear_tokens(self):
self.clear_cookie("session_token")
self.clear_all_cookies()
Secure Cookies
Since clients store cookies, they are vulnerable to tampering. Tornado mitigates this by cryptographically signing values. A secret key (cookie_secret) is required in the application configuration.
app = tornado.web.Application(
handlers=[...],
cookie_secret="a-random-secret-key-generated-for-production"
)
Use set_secure_cookie to sign data and get_secure_cookie to verify it.
class CounterHandler(RequestHandler):
def get(self):
count_val = self.get_secure_cookie("visits")
count = int(count_val) + 1 if count_val else 1
self.set_secure_cookie("visits", str(count), expires_days=1)
self.write(f"Total views: {count}")
The payload includes a timestamp and signature. Do not store sensitive PII in secure cookies; they prevent modification but do not encrypt data against network sniffing.
CSRF Protection
Cross-Site Request Forgery (CSRF) allows atackers to trick authenticated users into submitting unwanted requests. Tornado enforces Same-Origin Policy requirements for state-changing actions via tokens.
Configuration
Enable protection globally:
application = tornado.web.Application([
(r"/", PageHandler)
], xsrf_cookies=True)
This rejects POST, PUT, DELETE requests lacking a valid token argument.
Template Usage
When rendering HTML forms, inject the token automatically:
<form method="post">
{% module xsrf_form_html() %}
<input name="data" type="text">
<button>Submit</button>
</form>
This renders a hidden input field matching the _xsrf cookie value.
Non-Template Contexts
For AJAX requests or manual handling, retrieve the token manually:
function getXSRFToken() {
var x = document.cookie.split('; ').filter(row => row.startsWith('_xsrf='));
return x[0] ? x[0].split('=')[1] : null;
}
// Send via Header for JSON payloads
$.ajax({
url: '/api/process',
headers: {"X-XSRFToken": getXSRFToken()},
method: 'POST'
});
Alternatively, append _xsrf to the form body parameters for application/x-www-form-urlencoded requests.
Access Control
Authentication ensures only authorized users access specific resources.
Decorator Enforcement
Apply @authenticated to restrict handler access. If the user is unverified, GET requests redirect to a login URL.
class DashboardHandler(RequestHandler):
@tornado.web.authenticated
def get(self):
self.write("Welcome to your dashboard.")
Custom Logic
Override get_current_user() to define verification logic. Return the user identity or None.
def get_current_user(self):
token = self.get_secure_cookie("auth_token")
if token:
return decode_token(token)
return None
Redirection Settings
Configure where users land upon failing authentication check.
application = tornado.web.Application([
(r"/dashboard", DashboardHandler),
(r"/login", LoginView)
], login_url="/login")
Pass a next parameter during redirection to allow return navigation after successful login.