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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Declaring a schema centralizes validation and defaults so your view logic can trust its inputs.
- 2Building a query incrementally with conditional filters keeps optional parameters clean and composable.
- 3Mapping user-facing sort keys to explicit ORM expressions avoids injecting raw input into ordering.
Related explainers
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
rust
use chrono::{Duration, NaiveDate}; #[derive(Debug)] pub struct DateRange {
Parsing and iterating date ranges in Rust
error-handling
iterators
parsing
Intermediate
7 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/validating-query-params-in-flask-with-webargs-explained-python-5452/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.