python 55 lines · 10 steps

Validating query params in Flask with webargs

A schema-driven Flask endpoint that validates query strings, then builds a filtered, paginated product query.

Explained by highlit
1from flask import Blueprint, jsonify
2from marshmallow import Schema, fields, validate, EXCLUDE
3from webargs.flaskparser import use_args
4 
5from .models import Product
6 
7bp = Blueprint("catalog", __name__, url_prefix="/api")
8 
9 
10class ProductQuerySchema(Schema):
11 class Meta:
12 unknown = EXCLUDE
13 
14 q = fields.String(load_default="", validate=validate.Length(max=120))
15 category = fields.String(validate=validate.OneOf(["tools", "books", "food"]))
16 min_price = fields.Decimal(load_default=0, validate=validate.Range(min=0))
17 max_price = fields.Decimal(validate=validate.Range(min=0))
18 in_stock = fields.Boolean(load_default=False)
19 sort = fields.String(load_default="name", validate=validate.OneOf(["name", "price", "-price"]))
20 page = fields.Integer(load_default=1, validate=validate.Range(min=1))
21 per_page = fields.Integer(load_default=25, validate=validate.Range(min=1, max=100))
22 
23 
24@bp.get("/products")
25@use_args(ProductQuerySchema(), location="query")
26def list_products(args):
27 query = Product.query
28 
29 if args["q"]:
30 query = query.filter(Product.name.ilike(f"%{args['q']}%"))
31 if "category" in args:
32 query = query.filter_by(category=args["category"])
33 if args["in_stock"]:
34 query = query.filter(Product.stock > 0)
35 
36 query = query.filter(Product.price >= args["min_price"])
37 if "max_price" in args:
38 query = query.filter(Product.price <= args["max_price"])
39 
40 order = {
41 "name": Product.name.asc(),
42 "price": Product.price.asc(),
43 "-price": Product.price.desc(),
44 }[args["sort"]]
45 
46 page = query.order_by(order).paginate(
47 page=args["page"], per_page=args["per_page"], error_out=False
48 )
49 
50 return jsonify(
51 items=[p.to_dict() for p in page.items],
52 total=page.total,
53 page=page.page,
54 pages=page.pages,
55 )
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Declaring a schema centralizes validation and defaults so your view logic can trust its inputs.
  2. 2Building a query incrementally with conditional filters keeps optional parameters clean and composable.
  3. 3Mapping user-facing sort keys to explicit ORM expressions avoids injecting raw input into ordering.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Validating query params in Flask with webargs — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code