Skip to main content

🌊 Cuyutlán - Auto-Generate FastAPI Forms from Pydantic Models

Never write HTML forms again. Define your data model, get beautiful forms automatically.

PyPI version Python 3.9+ License: MIT

Cuyutlán (pronounced: koo-yoot-LAHN) - Named after the famous beach town in Colima, Mexico, known for its powerful waves. Like those waves, Cuyutlán streamlines your development flow! 🌊


🎯 The Problem

When building FastAPI apps, you face repetitive work:

  1. Define Pydantic model (your data structure)
  2. Write HTML form (duplicate the structure)
  3. Add client-side validation (duplicate the rules)
  4. Add server-side validation (already in Pydantic!)
  5. Style the form (tedious CSS)

Result: 90% of form code is boilerplate.


✨ The Solution: Cuyutlán

Write your Pydantic model once. Get everything automatically:

  • ✅ Beautiful HTML forms
  • ✅ Client-side validation (from Pydantic rules)
  • ✅ Server-side validation (already there!)
  • ✅ Responsive design (Bootstrap 5)
  • ✅ Type-specific widgets (date pickers, file uploads, etc.)
  • ✅ Custom templates (override what you need)

🚀 Quick Start

Installation

pip install cuyutlan

Basic Example (3 lines!)

from fastapi import FastAPI, Request
from pydantic import BaseModel, EmailStr, Field
from cuyutlan import render_form

app = FastAPI()

# 1. Define your data model (you're doing this anyway!)
class UserRegistration(BaseModel):
    username: str = Field(..., min_length=3, max_length=20)
    email: EmailStr
    age: int = Field(..., ge=18, le=120)
    newsletter: bool = False

# 2. Auto-generate form (ONE LINE!)
@app.get("/register")
async def register_form(request: Request):
    return render_form(UserRegistration, submit_url="/register", request=request)

# 3. Handle submission (standard FastAPI - no changes!)
@app.post("/register")
async def register_submit(user: UserRegistration):
    # Validation already done by FastAPI/Pydantic!
    return {"message": f"Welcome {user.username}!"}

That's it! You now have:

  • ✅ Beautiful form at /register
  • ✅ Client-side validation (min_length, email format, age range)
  • ✅ Server-side validation (automatic)
  • ✅ Error messages
  • ✅ Mobile responsive

🎨 What You Get

Type-Specific Widgets

Cuyutlán automatically selects the right HTML input based on type:

from pydantic import BaseModel, EmailStr, HttpUrl
from datetime import date, datetime
from typing import Literal

class SmartForm(BaseModel):
    # Text input
    name: str
    
    # Email input (with validation)
    email: EmailStr
    
    # Number input
    age: int
    
    # Date picker
    birth_date: date
    
    # Datetime picker
    appointment: datetime
    
    # Select dropdown
    role: Literal["admin", "user", "guest"]
    
    # Textarea
    bio: str = Field(..., max_length=500)
    
    # Checkbox
    agree_terms: bool
    
    # URL input
    website: HttpUrl

Validation from Pydantic

All Pydantic validators become HTML5 constraints:

class Product(BaseModel):
    name: str = Field(..., min_length=3, max_length=100)
    # → <input minlength="3" maxlength="100" required>
    
    price: float = Field(..., ge=0, le=10000)
    # → <input type="number" min="0" max="10000" required>
    
    sku: str = Field(..., pattern=r"^[A-Z]{3}-\d{4}$")
    # → <input pattern="^[A-Z]{3}-\d{4}$" required>
    
    email: EmailStr
    # → <input type="email" required>

🔥 Features

1. Type-to-Widget Mapping

Pydantic Type HTML Widget
str <input type="text">
int/float <input type="number">
EmailStr <input type="email">
bool <input type="checkbox">
date <input type="date">
Literal["a", "b"] <select> dropdown

2. Validation Mapping

Pydantic Constraint HTML5 Attribute
min_length=3 minlength="3"
max_length=20 maxlength="20"
ge=0 min="0"
pattern=r"..." pattern="..."

3. Custom Widgets

from cuyutlan import render_form

config = FormConfig(
    theme="dark",  # or "light"
    framework="bootstrap5",
)

return render_form(MyModel, config=config, request=request)

📊 Business Impact

Before Cuyutlán:

  • 2-3 hours per form
  • 100+ lines of HTML/JS
  • Frequent sync bugs

After Cuyutlán:

  • 5 minutes per form
  • 5 lines of code
  • Zero sync bugs

Savings: 90% time, 95% code, 100% bugs eliminated


🎯 Real-World Examples

Example 1: User Registration

class UserSignup(BaseModel):
    username: str = Field(..., min_length=3)
    email: EmailStr
    password: str = Field(..., min_length=8, widget="password")
    age: int = Field(..., ge=18)
    terms: bool = Field(..., description="I agree to terms")

@app.get("/signup")
async def signup_page(request: Request):
    return render_form(UserSignup, submit_url="/signup", request=request)

Example 2: Product Management

class Product(BaseModel):
    name: str = Field(..., max_length=200)
    description: str = Field(..., widget="textarea")
    price: float = Field(..., ge=0)
    category: Literal["electronics", "clothing", "food"]
    in_stock: bool = True

@app.get("/products/new")
async def new_product(request: Request):
    return render_form(Product, submit_url="/products", request=request)

🏗️ Use Cases

Perfect For:

  • ✅ Admin panels
  • ✅ Internal tools
  • ✅ CRUD applications
  • ✅ Rapid prototyping
  • ✅ MVP development
  • ✅ Form-heavy apps (surveys, registrations)

📖 Documentation

  • Quick Start: See above
  • Full Docs: (coming soon)
  • Examples: See examples/ folder
  • API Reference: (coming soon)

🤝 Contributing

Cuyutlán is open-source (MIT). Contributions welcome!

git clone https://github.com/carlos-robles/cuyutlan
cd cuyutlan
pip install -e ".[dev]"
pytest

📄 License

MIT License - See LICENSE file


🙏 Credits

Created by: Carlos Andres Robles Hernandez

Named after: Cuyutlán, Colima, Mexico 🇲🇽 - A beautiful beach town known for its powerful waves and laid-back vibe. This library aims to bring that same smooth, powerful flow to your FastAPI development!

Inspired by:

  • Django Forms (but for FastAPI)
  • Flask-WTF (but modern)
  • The FastAPI community

Built with:

  • FastAPI
  • Pydantic
  • Jinja2
  • Bootstrap 5

🔗 Links


Stop writing forms. Start building features. 🌊

¡Hecho en México con ❤️!

Metadata

Release files for cuyutlan 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cuyutlan 0.1.1
File Size Uploaded Provenance Actions
cuyutlan-0.1.1.tar.gz 12.8 kB Without provenance Details for cuyutlan-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cuyutlan 0.1.1
File Interpreter ABI Platform Actions
cuyutlan-0.1.1-py3-none-any.whl Python 3 none any Details for cuyutlan-0.1.1-py3-none-any.whl

Total release size: 26.6 kB

Release files / cuyutlan-0.1.1.tar.gz

Download URL cuyutlan-0.1.1.tar.gz
Size 12.8 kB
Tags Source
SHA-256 checksum
How to use checksums
050c960bd98d4a51e4579e39e57d30da44044cc248ac33bb50f85cdad5ed0d94
BLAKE2b-256 checksum
How to use checksums
5712faa6fbac8e78eb774429349fe227569ebfa13cf173dcb9f8461b0f8f79c3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.5

Release files / cuyutlan-0.1.1-py3-none-any.whl

Download URL cuyutlan-0.1.1-py3-none-any.whl
Size 13.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a3c779144b22c1b91e6f0fcc8552cab2300aa1a432a63f5d7e183ee1bb98e1ba
BLAKE2b-256 checksum
How to use checksums
b0bb5738d35d5da197cb04aa7a675b771b72805389faaec80a2f0148c9123bee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.5

Security for cuyutlan 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page