Part 3 — Forms
Plain Form
Section titled “Plain Form”from buraq.forms import Formfrom buraq.forms.fields import CharField, TextField, EmailFieldfrom buraq.exceptions import ValidationError
class CommentForm(Form): author_name = CharField(max_length=100, label="Your name") email = EmailField(required=False, label="Email (optional)") body = TextField(label="Comment")
def clean_body(self, value): if len(value.strip()) < 5: raise ValidationError("Comment must be at least 5 characters.") return value.strip()ModelForm
Section titled “ModelForm”Auto-generates fields from model columns:
from buraq.forms import ModelFormfrom buraq.exceptions import ValidationErrorfrom posts.models import Post
class PostForm(ModelForm): class Meta: model = Post fields = ["title", "slug", "content", "is_published"]
def clean_slug(self, value): if " " in value: raise ValidationError("Slug must not contain spaces.") return value.lower()
async def clean(self): data = self._cleaned_data if data.get("is_published") and not data.get("content"): self.add_error("content", "Cannot publish without content.") return dataUsing forms in views
Section titled “Using forms in views”from buraq.shortcuts import render, redirectfrom posts.forms import CommentForm, PostFormfrom posts.models import Post
async def create_post(request): if request.method == "POST": form = PostForm(data=dict(await request.form())) if await form.is_valid(): post = await form.save() return redirect(f"/posts/{post.slug}") else: form = PostForm()
return await render(request, "posts/form.html", {"form": form})
async def edit_post(request, pk: int): post = await Post.objects.get(id=pk) if request.method == "POST": form = PostForm(data=dict(await request.form()), instance=post) if await form.is_valid(): await form.save() return redirect(f"/posts/{post.slug}") else: form = PostForm(instance=post) # pre-fills fields from instance
return await render(request, "posts/form.html", {"form": form})Rendering forms in templates
Section titled “Rendering forms in templates”<form method="post"> {% for field in form %} <div class="field"> <label for="{{ field.html_name }}">{{ field.label }}</label>
{% if field.errors %} {% for err in field.errors %} <p class="error">{{ err }}</p> {% endfor %} {% endif %}
<input type="text" name="{{ field.html_name }}" id="{{ field.html_name }}" value="{{ field.value }}"> </div> {% endfor %}
<button type="submit">Save</button></form>Validation flow
Section titled “Validation flow”field.clean(raw_value)— converts type, checks required, runs field validatorsform.clean_<fieldname>(value)— per-field custom validationform.clean()— cross-field validation (can beasync def)
Next: Templates →