Challenge 1: Write a ModelForm — Possible Solution ==================================================================== # catalog/forms.py from django import forms from .models import Author class AuthorForm(forms.ModelForm): class Meta: model = Author fields = ["name", "bio"] # birth_year deliberately excluded # catalog/views.py from django.shortcuts import render, redirect from .forms import AuthorForm def create_author(request): if request.method == "POST": form = AuthorForm(request.POST) if form.is_valid(): form.save() return redirect("author-list") else: form = AuthorForm() return render(request, "catalog/author_form.html", {"form": form})
{% csrf_token %} {{ form.as_p }}
WHY THIS WORKS -------------- - Meta.fields = ["name", "bio"] is an explicit allowlist — even though Author has a birth_year field, this form has no way to set it, because ModelForm only generates fields for what's listed here. Submitting a birth_year value through this form's POST data is simply ignored. - The view follows the same GET/POST branching pattern from Chapter 3's FBVs: GET shows an unbound (empty) form, POST binds the submitted data to the form and checks is_valid() before saving. - form.save() does double duty: it both validates one more time (though is_valid() already ran) and creates + persists a new Author row using the validated, cleaned data — no need to manually construct Author(name=..., bio=...) and call .save() on it separately. - {% csrf_token %} is present in the template, without which this POST form would fail with a 403 regardless of how correct the form and view logic are — exactly the chapter's gotcha.