python
38 lines · 6 steps
Custom URL converters in Flask
A custom slug converter validates and normalizes URL segments before your view functions ever run.
Explained by
highlit
1from werkzeug.routing import BaseConverter, ValidationError
2from flask import Flask, jsonify, abort
3
4
5class SlugConverter(BaseConverter):
6 regex = r"[a-z0-9]+(?:-[a-z0-9]+)*"
7
8 def to_python(self, value):
9 if len(value) > 200:
10 raise ValidationError()
11 return value
12
13 def to_url(self, value):
14 slug = value.strip().lower().replace(" ", "-")
15 return super().to_url(slug)
16
17
18app = Flask(__name__)
19app.url_map.converters["slug"] = SlugConverter
20
21
22@app.route("/articles/<slug:slug>")
23def show_article(slug):
24 article = Article.query.filter_by(slug=slug).first()
25 if article is None:
26 abort(404)
27 return jsonify(article.to_dict())
28
29
30@app.route("/categories/<slug:category>/<slug:slug>")
31def show_in_category(category, slug):
32 article = (
33 Article.query
34 .join(Category)
35 .filter(Category.slug == category, Article.slug == slug)
36 .first_or_404()
37 )
38 return jsonify(article.to_dict())
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A converter's regex filters requests at the routing layer, so bad URLs never reach your view.
- 2to_python and to_url let you validate incoming segments and normalize outgoing ones symmetrically.
- 3Registering a converter by name makes it reusable across every route as <name:variable>.
Related explainers
typescript
import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { ValidationPipe } from '@nestjs/common'; import { ApiProperty } from '@nestjs/swagger';
Wiring validation and Swagger docs in NestJS
validation
openapi
decorators
Intermediate
8 steps
python
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from pydantic import BaseModel, EmailStr from sqlalchemy.orm import Session
Building a signup endpoint in FastAPI
dependency-injection
request-validation
background-tasks
Intermediate
8 steps
ruby
class TemplateInterpolator PLACEHOLDER = /\{\{\s*([\w.]+)\s*\}\}/ def initialize(strict: false)
Interpolating templates with dotted keys in Ruby
regex
string-interpolation
hash-traversal
Intermediate
6 steps
python
from django import forms from django.utils import timezone from .models import Reservation
Multi-field validation in a Django ModelForm
form validation
cross-field validation
modelform
Intermediate
7 steps
python
from django import template from django.urls import reverse, NoReverseMatch from django.utils.html import format_html
Active nav-link template tags in Django
template tags
url routing
active state
Intermediate
7 steps
python
from functools import wraps import asyncio from fastapi import APIRouter, FastAPI, Request
Per-route request timeouts in FastAPI
decorators
async
timeouts
Intermediate
6 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/custom-url-converters-in-flask-explained-python-f05b/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.