The Add-Item Form Outside the Admin: Django Forms & Type-Specific Fields
Personal Catalogue: Django & PostgreSQL
Chapter 5 · The Add-Item Form Outside the Admin: Django Forms & Type-Specific Fields
Chapter 4 gave the catalogue real public list and detail pages, but adding an item still means logging into /admin/. This chapter builds a real, public-facing add-item form — and makes that one shared form feel genuinely type-aware, without forking it into four separate forms.
The Core Idea: Data-Driven Labels, Not a Fork in the Form
A tempting but wrong approach here is to build a genuinely separate form for each item type — a "book form," a "CD form," a "DVD form" — each with its own field names. That would mean four times the template markup to maintain, and four separate view branches to validate and save. Since the underlying Item model is deliberately shared (Chapter 2), the form stays shared too: one set of fields, with their labels — not their underlying model fields — changing based on whatever item type is currently selected.
| Field | Book | CD | DVD / Blu-ray |
|---|---|---|---|
creator | Author | Artist | Director |
format_detail | Hardcover / paperback | Single / album | Region code |
A Real Django ModelForm
A ModelForm derives its own fields, widgets, and — critically — its own validation rules directly from the Item model itself, rather than requiring them to be redeclared by hand. release_year's own real PositiveSmallIntegerField constraint from Chapter 2 is enforced automatically here, with no extra validation code needed.
The CreateView
Add the URL to catalogue/urls.py:
Building the Label Map with json_script
Rather than hand-typing a JSON string directly into the template — a real, documented cross-site-scripting risk if any of that data ever contains untrusted content — Django provides a dedicated template filter built specifically for this exact job:
{{ creator_labels|json_script:"creator-labels-data" }} renders a real <script id="creator-labels-data" type="application/json"> tag, with its own JSON content correctly HTML-escaped by Django — safe to include directly in a template even if the underlying data were ever user-controlled, unlike manually interpolating a JSON string into a page. It's the Django-native equivalent of what PHP Intermediate's own json_encode() technique accomplishes in the PHP variant of this project (catalog-php1) — the same underlying goal, real server-rendered data handed safely to client-side JavaScript, achieved through Django's own dedicated tooling rather than a manually-built inline script.
Wiring the Dropdown to the Labels
A small vanilla JS block reads the two JSON script tags and updates the form's own labels whenever the item type dropdown changes:
id_item_type and id_creator are Django's own default, automatically-generated field IDs — {{ form.as_p }} already produces them without any extra configuration, which is why the JS above can rely on them directly.
ItemForm validation on the server, defined once in Chapter 2's own model constraints and re-used automatically by the ModelForm. The label-swapping script is a real usability improvement, not a security or validation mechanism — the server-side form is what actually enforces the rules, exactly as it does in the PHP variant of this project.
Trying It Out
Visit /add/, select each item type in turn, and confirm the "Creator" and "Format detail" labels genuinely change to Author/Artist/Director and the matching format hint. Submit a real book with a title, author, and at least one tag, then confirm it appears correctly on the list page from Chapter 4.
Hands-On Exercises
Build the form, view, template, and label-swapping script from this chapter, then add one real item of each of the four types through this new public form.
📄 View solutionExplain what json_script actually produces in the rendered HTML, and why it's genuinely safer than building the equivalent <script> tag by hand-interpolating a JSON string into the template.
Explain why the label-swapping JavaScript from this chapter is not, and cannot be, a substitute for the server-side validation the ModelForm already provides.
Chapter 5 Quick Reference
- ItemForm — a real
ModelFormderiving its fields and validation directly from theItemmodel - ItemCreateView — Django's own generic
CreateView, redirecting to the list page viasuccess_urlon success - One shared form, several labels — labels change per item type via JavaScript; the underlying fields and their names never change
- json_script — Django's own dedicated, safely-escaped way to hand server-rendered data to client-side JavaScript
- Real security note — client-side relabeling is cosmetic only; the
ModelForm's own server-side validation is what actually enforces the rules - Next chapter: Tags — filtering the catalogue by tag