Skip to content

ModelForm

ModelForm auto-generates form fields from your model’s columns and handles save() for you.

class PostForm(ModelForm):
class Meta:
model = Post
fields = ["title", "slug", "content", "is_published"]

fields controls which columns become form fields. Use "__all__" to include every column (except id).

class PostForm(ModelForm):
class Meta:
model = Post
exclude = ["created_at", "updated_at"]

Declare a field explicitly to override the auto-generated one:

class PostForm(ModelForm):
title = CharField(max_length=200, label="Post Title", help_text="Keep it concise.")
class Meta:
model = Post
fields = ["title", "slug", "content"]
SQLAlchemy type Form field
String CharField
Text TextField
Integer IntegerField
Float FloatField
Numeric DecimalField
Boolean BooleanField
Date DateField
DateTime DateTimeField
form = PostForm(data=dict(await request.form()))
if await form.is_valid():
post = await form.save() # creates a new Post in the database

Pass instance to pre-fill the form and update instead of create:

post = await Post.objects.get(id=pk)
# GET — display pre-filled form
form = PostForm(instance=post)
# POST — validate and save
form = PostForm(data=dict(await request.form()), instance=post)
if await form.is_valid():
await form.save() # updates existing post
if await form.is_valid():
post = await form.save(commit=False)
post.author_id = request.user.id # set extra fields
await post.save()

Render a form directly to HTML without writing a template loop:

form = PostForm()
form.as_p() # each field wrapped in <p>
form.as_table() # each field as a <tr> (wrap in <table> yourself)
form.as_div() # each field in <div class="form-group">

Each method emits labels, widgets, and inline error lists. Useful for quick prototypes or email bodies; use a custom template loop for full control over markup.