FastAPI wrapper to create Django like experience which uses Piccolo ORM under the hood.
Project description
⚡ Fastique
Django-like developer experience, powered by FastAPI + Piccolo ORM.
Fastique gives you the familiar Django workflow — startproject, startapp, migrate, createsuperuser, class-based views, an auto-generated admin panel — all running on the blazing-fast async FastAPI + Piccolo ORM stack.
Feature Overview
| Django feature | Fastique equivalent |
|---|---|
django-admin startproject |
fq startproject |
python manage.py startapp |
fq startapp |
python manage.py runserver |
fq runserver |
python manage.py makemigrations |
fq makemigrations |
python manage.py migrate |
fq migrate |
python manage.py createsuperuser |
fq createsuperuser |
python manage.py shell |
fq shell |
python manage.py test |
fq test |
models.Model |
fastique.Model (Piccolo Table) |
admin.site.register |
@register(MyModel) |
ModelAdmin |
fastique.ModelAdmin |
ListView, DetailView, etc. |
Same names, async |
settings.py |
Same — loaded via FASTIQUE_SETTINGS_MODULE |
urls.py / include() |
Router + include() |
@login_required |
Same |
| JWT tokens | Built-in via /auth/token |
| Session auth | Built-in via /auth/login |
Installation
pip install fastique
Quickstart
1. Create a project
fq startproject myblog
cd myblog
Generated structure:
myblog/
├── manage.py ← entry point (like Django's)
├── myblog/
│ ├── __init__.py
│ ├── settings.py ← all settings in one place
│ ├── asgi.py ← ASGI app (points uvicorn here)
│ └── urls.py ← project-level router hints
├── templates/
│ └── base.html
├── static/
├── media/
├── piccolo_conf.py ← Piccolo migration config
├── .env ← environment variables
├── requirements.txt
└── README.md
2. Create an app
fq startapp blog
Generated app structure:
blog/
├── __init__.py
├── models.py ← your Piccolo Table models
├── routes.py ← FastAPI router (like views.py + urls.py)
├── admin.py ← register models with the admin
├── schemas.py ← Pydantic request/response schemas
├── services.py ← business logic layer
├── tests.py ← pytest tests
├── piccolo_app.py ← Piccolo migration config for this app
└── migrations/ ← auto-generated migration files
3. Register the app
# myblog/settings.py
INSTALLED_APPS = [
"blog",
]
4. Define models
# blog/models.py
from fastique import Model, CharField, TextField, BooleanField, DateTimeField, ForeignKey
from fastique.auth.models import User
from datetime import datetime, timezone
class Article(Model):
title = CharField(max_length=200)
slug = CharField(max_length=220, unique=True)
body = TextField()
author = ForeignKey(references=User)
is_published = BooleanField(default=False)
created_at = DateTimeField(default=datetime.now(tz=timezone.utc))
class Meta:
tablename = "blog_articles"
ordering = ["-created_at"]
def __str__(self):
return self.title
5. Register with admin
# blog/admin.py
from fastique import register, ModelAdmin
from blog.models import Article
@register(Article)
class ArticleAdmin(ModelAdmin):
list_display = ["id", "title", "author", "is_published", "created_at"]
list_filter = ["is_published"]
search_fields = ["title", "body"]
ordering = ["-created_at"]
readonly_fields = ["created_at"]
6. Write routes
# blog/routes.py
from fastique import Router, login_required
from starlette.requests import Request
from starlette.responses import JSONResponse
from blog.models import Article
from blog.schemas import ArticleSchema, ArticleCreateSchema
router = Router(prefix="/blog", tags=["Blog"])
@router.get("/")
async def list_articles(request: Request, page: int = 1) -> JSONResponse:
articles = await Article.select().output(load_json=True)
return JSONResponse({"articles": articles})
@router.get("/{slug}")
async def get_article(slug: str) -> JSONResponse:
rows = await Article.select().where(Article.slug == slug).output(load_json=True)
if not rows:
return JSONResponse({"detail": "Not found."}, status_code=404)
return JSONResponse(rows[0])
@router.post("/", status_code=201)
@login_required
async def create_article(request: Request, data: ArticleCreateSchema) -> JSONResponse:
article = Article(**data.model_dump())
await article.save().run()
return JSONResponse({"id": article.id, "detail": "Created."})
7. Run migrations
fq makemigrations blog # generate migration file
fq migrate blog # apply it
8. Create a superuser
fq createsuperuser
# Username: admin
# Email: admin@example.com
# Password: ••••••••
9. Start the server
fq runserver
╭──────────────────────────────────────────╮
│ Starting Fastique development server │
│ http://127.0.0.1:8000 │
│ Admin: http://127.0.0.1:8000/admin/ │
│ API Docs: http://127.0.0.1:8000/docs │
│ Quit with CTRL+C │
╰──────────────────────────────────────────╯
Models
from fastique import (
Model,
CharField, TextField, IntegerField, FloatField, BooleanField,
DateField, DateTimeField, ForeignKey, ManyToManyField,
EmailField, URLField, UUIDField, JSONField,
)
QuerySet API
# ORM-style (async)
articles = await Article.objects.all()
article = await Article.objects.get(id=1) # raises Http404 if missing
articles = await Article.objects.filter(is_published=True)
article, created = await Article.objects.get_or_create(slug="hello", defaults={"title": "Hello"})
article = await Article.objects.create(title="New Post", slug="new-post")
# Piccolo native (more expressive)
articles = await Article.select().where(Article.is_published == True).order_by(Article.created_at, ascending=False).output(load_json=True)
Class-Based Views
from fastique import ListView, DetailView, CreateView, UpdateView, DeleteView
class ArticleList(ListView):
model = Article
template_name = "blog/list.html"
paginate_by = 10
ordering = "-created_at"
class ArticleDetail(DetailView):
model = Article
template_name = "blog/detail.html"
lookup_field = "slug"
# Register with router:
router.get("/")(ArticleList.as_route())
router.get("/{slug}")(ArticleDetail.as_route())
Authentication
Session-based
POST /auth/login {"username": "...", "password": "..."}
POST /auth/logout
GET /auth/me
POST /auth/register {"username": "...", "email": "...", "password": "..."}
JWT / Token
POST /auth/token {"username": "...", "password": "..."}
# Returns: {"access_token": "eyJ...", "token_type": "bearer"}
# Use in headers:
Authorization: Bearer eyJ...
Decorators
from fastique import login_required, staff_required, superuser_required, permission_required
@router.get("/dashboard")
@login_required
async def dashboard(request): ...
@router.get("/admin-only")
@staff_required
async def admin_view(request): ...
@router.delete("/nuke")
@superuser_required
async def dangerous(request): ...
@router.post("/publish")
@permission_required("blog.publish_article")
async def publish(request): ...
Settings Reference
# settings.py
SECRET_KEY = "your-secret-key"
DEBUG = True
ALLOWED_HOSTS = ["*"]
INSTALLED_APPS = ["blog", "shop"]
DATABASES = {
"default": {
"ENGINE": "sqlite", # or "postgresql"
"NAME": "db.sqlite3",
}
}
# PostgreSQL:
# DATABASES = {
# "default": {
# "ENGINE": "postgresql",
# "NAME": "mydb",
# "USER": "postgres",
# "PASSWORD": "secret",
# "HOST": "localhost",
# "PORT": 5432,
# }
# }
TEMPLATES = [{"BACKEND": "jinja2", "DIRS": ["templates"], "APP_DIRS": True}]
STATIC_URL = "/static/"
STATIC_ROOT = "staticfiles"
MEDIA_URL = "/media/"
MEDIA_ROOT = "media"
CORS_ALLOW_ALL_ORIGINS = True # dev only
CORS_ALLOWED_ORIGINS = ["https://myfrontend.com"]
ADMIN_URL = "admin/"
ADMIN_SITE_HEADER = "My Site Admin"
ACCESS_TOKEN_EXPIRE_MINUTES = 1440 # 24 hours
CLI Reference
fq startproject <name> Create a new project
fq startapp <name> Create a new app
fq runserver [host:port] Run dev server (default: 127.0.0.1:8000)
fq makemigrations [app] Generate migration files
fq migrate [app] Apply pending migrations
fq createsuperuser Create a superuser interactively
fq shell Interactive Python shell with context
fq dbshell Open the database CLI
fq collectstatic Copy static files to STATIC_ROOT
fq check Run system checks
fq showmigrations [app] Show migration status
fq routes List all registered routes
fq test [args...] Run pytest
fq version Show version info
Project Layout (full example)
myproject/
├── manage.py
├── myproject/
│ ├── settings.py
│ ├── asgi.py
│ └── urls.py
├── blog/
│ ├── models.py
│ ├── routes.py
│ ├── admin.py
│ ├── schemas.py
│ ├── services.py
│ ├── tests.py
│ ├── piccolo_app.py
│ ├── migrations/
│ └── templates/
│ └── blog/
│ ├── list.html
│ └── detail.html
├── templates/
│ └── base.html
├── static/
├── media/
├── piccolo_conf.py
├── .env
└── requirements.txt
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastique-0.1.0.tar.gz.
File metadata
- Download URL: fastique-0.1.0.tar.gz
- Upload date:
- Size: 33.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61a24e383cc88b170a05cd988071a147e5e31d2b656b005c442205b59c9bfdfb
|
|
| MD5 |
06c43bd5134b0d3862e526cd3af1bfee
|
|
| BLAKE2b-256 |
63fd91cc730138e3099fc0e650e04322643b78c585248192e9540693d7160e7a
|
File details
Details for the file fastique-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fastique-0.1.0-py3-none-any.whl
- Upload date:
- Size: 46.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fadd265af33d929899fd391ee25a2a34a0100f22b8b3a012aa980eaba87c3034
|
|
| MD5 |
03da534b7be8a25f93da727f58ea76c3
|
|
| BLAKE2b-256 |
c343773b3b581588a20c5436d4014069429430ae28d905799a4da3d9bfb1b98c
|