CINXE.COM

Django Tutorial Part 9: Working with forms - Learn web development | MDN

<!doctype html><html lang="en-US" prefix="og: https://ogp.me/ns#"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="icon" href="https://developer.mozilla.org/favicon-48x48.bc390275e955dacb2e65.png"/><link rel="apple-touch-icon" href="https://developer.mozilla.org/apple-touch-icon.528534bba673c38049c2.png"/><meta name="theme-color" content="#ffffff"/><link rel="manifest" href="https://developer.mozilla.org/manifest.f42880861b394dd4dc9b.json"/><link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="MDN Web Docs"/><title>Django Tutorial Part 9: Working with forms - Learn web development | MDN</title><link rel="alternate" title="Django Tutorial Teil 9: Arbeiten mit Formularen" href="https://developer.mozilla.org/de/docs/Learn_web_development/Extensions/Server-side/Django/Forms" hrefLang="de"/><link rel="alternate" title="Tutorial de Django Parte 9: Trabajo con formularios" href="https://developer.mozilla.org/es/docs/Learn_web_development/Extensions/Server-side/Django/Forms" hrefLang="es"/><link rel="alternate" title="Django didactique - Section 9 : Travailler avec des formulaires" href="https://developer.mozilla.org/fr/docs/Learn_web_development/Extensions/Server-side/Django/Forms" hrefLang="fr"/><link rel="alternate" title="Django 튜토리얼 파트 9: 폼(form)으로 작업하기" href="https://developer.mozilla.org/ko/docs/Learn_web_development/Extensions/Server-side/Django/Forms" hrefLang="ko"/><link rel="alternate" title="Tutorial Django Parte 9: Trabalhando com formulários" href="https://developer.mozilla.org/pt-BR/docs/Learn_web_development/Extensions/Server-side/Django/Forms" hrefLang="pt"/><link rel="alternate" title="Руководство часть 9: Работа с формами" href="https://developer.mozilla.org/ru/docs/Learn_web_development/Extensions/Server-side/Django/Forms" hrefLang="ru"/><link rel="alternate" title="Django 教程 9: 使用表单" href="https://developer.mozilla.org/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Forms" hrefLang="zh"/><link rel="alternate" title="Django Tutorial Part 9: Working with forms" href="https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Extensions/Server-side/Django/Forms" hrefLang="zh-Hant"/><link rel="alternate" title="Django Tutorial Part 9: Working with forms" href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms" hrefLang="en"/><link rel="preload" as="font" type="font/woff2" href="/static/media/Inter.var.c2fe3cb2b7c746f7966a.woff2" crossorigin=""/><link rel="alternate" type="application/rss+xml" title="MDN Blog RSS Feed" href="https://developer.mozilla.org/en-US/blog/rss.xml" hrefLang="en"/><meta name="description" content="In this tutorial, we&#x27;ll show you how to work with HTML Forms in Django, and, in particular, the easiest way to write forms to create, update, and delete model instances. As part of this demonstration, we&#x27;ll extend the LocalLibrary website so that librarians can renew books, and create, update, and delete authors using our own forms (rather than using the admin application)."/><meta property="og:url" content="https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms"/><meta property="og:title" content="Django Tutorial Part 9: Working with forms - Learn web development | MDN"/><meta property="og:type" content="website"/><meta property="og:locale" content="en_US"/><meta property="og:description" content="In this tutorial, we&#x27;ll show you how to work with HTML Forms in Django, and, in particular, the easiest way to write forms to create, update, and delete model instances. As part of this demonstration, we&#x27;ll extend the LocalLibrary website so that librarians can renew books, and create, update, and delete authors using our own forms (rather than using the admin application)."/><meta property="og:image" content="https://developer.mozilla.org/mdn-social-share.d893525a4fb5fb1f67a2.png"/><meta property="og:image:type" content="image/png"/><meta property="og:image:height" content="1080"/><meta property="og:image:width" content="1920"/><meta property="og:image:alt" content="The MDN Web Docs logo, featuring a blue accent color, displayed on a solid black background."/><meta property="og:site_name" content="MDN Web Docs"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:creator" content="MozDevNet"/><link rel="canonical" href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms"/><style media="print">.article-actions-container,.document-toc-container,.language-menu,.main-menu-toggle,.on-github,.page-footer,.place,.sidebar,.top-banner,.top-navigation-main,ul.prev-next{display:none!important}.main-page-content,.main-page-content pre{padding:2px}.main-page-content pre{border-left-width:2px}</style><script src="/static/js/gtag.js" defer=""></script><script defer="" src="/static/js/main.f565372a.js"></script><link href="/static/css/main.3d9e7a02.css" rel="stylesheet"/></head><body><script>if(document.body.addEventListener("load",(t=>{t.target.classList.contains("interactive")&&t.target.setAttribute("data-readystate","complete")}),{capture:!0}),window&&document.documentElement){const t={light:"#ffffff",dark:"#1b1b1b"};try{const e=window.localStorage.getItem("theme");e&&(document.documentElement.className=e,document.documentElement.style.backgroundColor=t[e]);const o=window.localStorage.getItem("nop");o&&(document.documentElement.dataset.nop=o)}catch(t){console.warn("Unable to read theme from localStorage",t)}}</script><div id="root"><ul id="nav-access" class="a11y-nav"><li><a id="skip-main" href="#content">Skip to main content</a></li><li><a id="skip-search" href="#top-nav-search-input">Skip to search</a></li><li><a id="skip-select-language" href="#languages-switcher-button">Skip to select language</a></li></ul><div class="page-wrapper category-learn document-page"><div class="top-banner loading"><section class="place top container"></section></div><div class="sticky-header-container"><header class="top-navigation "><div class="container "><div class="top-navigation-wrap"><a href="/en-US/" class="logo" aria-label="MDN homepage"><svg id="mdn-docs-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 694.9 104.4" style="enable-background:new 0 0 694.9 104.4" xml:space="preserve" role="img"><title>MDN Web Docs</title><path d="M40.3 0 11.7 92.1H0L28.5 0h11.8zm10.4 0v92.1H40.3V0h10.4zM91 0 62.5 92.1H50.8L79.3 0H91zm10.4 0v92.1H91V0h10.4z" class="logo-m"></path><path d="M627.9 95.6h67v8.8h-67v-8.8z" class="logo-_"></path><path d="M367 42h-4l-10.7 30.8h-5.5l-10.8-26h-.4l-10.5 26h-5.2L308.7 42h-3.8v-5.6H323V42h-6.5l6.8 20.4h.4l10.3-26h4.7l11.2 26h.5l5.7-20.3h-6.2v-5.6H367V42zm34.9 20c-.4 3.2-2 5.9-4.7 8.2-2.8 2.3-6.5 3.4-11.3 3.4-5.4 0-9.7-1.6-13.1-4.7-3.3-3.2-5-7.7-5-13.7 0-5.7 1.6-10.3 4.7-14s7.4-5.5 12.9-5.5c5.1 0 9.1 1.6 11.9 4.7s4.3 6.9 4.3 11.3c0 1.5-.2 3-.5 4.7h-25.6c.3 7.7 4 11.6 10.9 11.6 2.9 0 5.1-.7 6.5-2 1.5-1.4 2.5-3 3-4.9l6 .9zM394 51.3c.2-2.4-.4-4.7-1.8-6.9s-3.8-3.3-7-3.3c-3.1 0-5.3 1-6.9 3-1.5 2-2.5 4.4-2.8 7.2H394zm51 2.4c0 5-1.3 9.5-4 13.7s-6.9 6.2-12.7 6.2c-6 0-10.3-2.2-12.7-6.7-.1.4-.2 1.4-.4 2.9s-.3 2.5-.4 2.9h-7.3c.3-1.7.6-3.5.8-5.3.3-1.8.4-3.7.4-5.5V22.3h-6v-5.6H416v27c1.1-2.2 2.7-4.1 4.7-5.7 2-1.6 4.8-2.4 8.4-2.4 4.6 0 8.4 1.6 11.4 4.7 3 3.2 4.5 7.6 4.5 13.4zm-7.7.6c0-4.2-1-7.4-3-9.5-2-2.2-4.4-3.3-7.4-3.3-3.4 0-6 1.2-8 3.7-1.9 2.4-2.9 5-3 7.7V57c0 3 1 5.6 3 7.7s4.5 3.1 7.6 3.1c3.6 0 6.3-1.3 8.1-3.9 1.8-2.7 2.7-5.9 2.7-9.6zm69.2 18.5h-13.2v-7.2c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2 5.7 0 9.8 2.2 12.3 6.5V22.3h-8.6v-5.6h15.8v50.6h6v5.5zM493.2 56v-4.4c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm53.1-1.4c0 5.6-1.8 10.2-5.3 13.7s-8.2 5.3-13.9 5.3-10.1-1.7-13.4-5.1c-3.3-3.4-5-7.9-5-13.5 0-5.3 1.6-9.9 4.7-13.7 3.2-3.8 7.9-5.7 14.2-5.7s11 1.9 14.1 5.7c3 3.7 4.6 8.1 4.6 13.3zm-7.7-.2c0-4-1-7.2-3-9.5s-4.8-3.5-8.2-3.5c-3.6 0-6.4 1.2-8.3 3.7s-2.9 5.6-2.9 9.5c0 3.7.9 6.8 2.8 9.4 1.9 2.6 4.6 3.9 8.3 3.9 3.6 0 6.4-1.3 8.4-3.8 1.9-2.6 2.9-5.8 2.9-9.7zm45 5.8c-.4 3.2-1.9 6.3-4.4 9.1-2.5 2.9-6.4 4.3-11.8 4.3-5.2 0-9.4-1.6-12.6-4.8-3.2-3.2-4.8-7.7-4.8-13.7 0-5.5 1.6-10.1 4.7-13.9 3.2-3.8 7.6-5.7 13.2-5.7 2.3 0 4.6.3 6.7.8 2.2.5 4.2 1.5 6.2 2.9l1.5 9.5-5.9.7-1.3-6.1c-2.1-1.2-4.5-1.8-7.2-1.8-3.5 0-6.1 1.2-7.7 3.7-1.7 2.5-2.5 5.7-2.5 9.6 0 4.1.9 7.3 2.7 9.5 1.8 2.3 4.4 3.4 7.8 3.4 5.2 0 8.2-2.9 9.2-8.8l6.2 1.3zm34.7 1.9c0 3.6-1.5 6.5-4.6 8.5s-7 3-11.7 3c-5.7 0-10.6-1.2-14.6-3.6l1.2-8.8 5.7.6-.2 4.7c1.1.5 2.3.9 3.6 1.1s2.6.3 3.9.3c2.4 0 4.5-.4 6.5-1.3 1.9-.9 2.9-2.2 2.9-4.1 0-1.8-.8-3.1-2.3-3.8s-3.5-1.3-5.8-1.7-4.6-.9-6.9-1.4c-2.3-.6-4.2-1.6-5.7-2.9-1.6-1.4-2.3-3.5-2.3-6.3 0-4.1 1.5-6.9 4.6-8.5s6.4-2.4 9.9-2.4c2.6 0 5 .3 7.2.9 2.2.6 4.3 1.4 6.1 2.4l.8 8.8-5.8.7-.8-5.7c-2.3-1-4.7-1.6-7.2-1.6-2.1 0-3.7.4-5.1 1.1-1.3.8-2 2-2 3.8 0 1.7.8 2.9 2.3 3.6 1.5.7 3.4 1.2 5.7 1.6 2.2.4 4.5.8 6.7 1.4 2.2.6 4.1 1.6 5.7 3 1.4 1.6 2.2 3.7 2.2 6.6zM197.6 73.2h-17.1v-5.5h3.8V51.9c0-3.7-.7-6.3-2.1-7.9-1.4-1.6-3.3-2.3-5.7-2.3-3.2 0-5.6 1.1-7.2 3.4s-2.4 4.6-2.5 6.9v15.6h6v5.5h-17.1v-5.5h3.8V51.9c0-3.8-.7-6.4-2.1-7.9-1.4-1.5-3.3-2.3-5.6-2.3-3.2 0-5.5 1.1-7.2 3.3-1.6 2.2-2.4 4.5-2.5 6.9v15.8h6.9v5.5h-20.2v-5.5h6V42.4h-6.1v-5.6h13.4v6.4c1.2-2.1 2.7-3.8 4.7-5.2 2-1.3 4.4-2 7.3-2s5.3.7 7.5 2.1c2.2 1.4 3.7 3.5 4.5 6.4 1.1-2.5 2.7-4.5 4.9-6.1s4.8-2.4 7.9-2.4c3.5 0 6.5 1.1 8.9 3.3s3.7 5.6 3.7 10.2v18.2h6.1v5.5zm42.5 0h-13.2V66c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2s9.8 2.2 12.3 6.5V22.7h-8.6v-5.6h15.8v50.6h6v5.5zm-13.3-16.8V52c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm61.5 16.8H269v-5.5h6V51.9c0-3.7-.7-6.3-2.2-7.9-1.4-1.6-3.4-2.3-5.7-2.3-3.1 0-5.6 1-7.4 3s-2.8 4.4-2.9 7v15.9h6v5.5h-19.3v-5.5h6V42.4h-6.2v-5.6h13.6V43c2.6-4.6 6.8-6.9 12.7-6.9 3.6 0 6.7 1.1 9.2 3.3s3.7 5.6 3.7 10.2v18.2h6v5.4h-.2z" class="logo-text"></path></svg></a><button title="Open main menu" type="button" class="button action has-icon main-menu-toggle" aria-haspopup="menu" aria-label="Open main menu" aria-expanded="false"><span class="button-wrap"><span class="icon icon-menu "></span><span class="visually-hidden">Open main menu</span></span></button></div><div class="top-navigation-main"><nav class="main-nav" aria-label="Main menu"><ul class="main-menu nojs"><li class="top-level-entry-container "><button type="button" id="references-button" class="top-level-entry menu-toggle" aria-controls="references-menu" aria-expanded="false">References</button><a href="/en-US/docs/Web" class="top-level-entry">References</a><ul id="references-menu" class="submenu references hidden inline-submenu-lg" aria-labelledby="references-button"><li class="apis-link-container mobile-only "><a href="/en-US/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li><li class="html-link-container "><a href="/en-US/docs/Web/HTML" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Structure of content on the web</p></div></a></li><li class="css-link-container "><a href="/en-US/docs/Web/CSS" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Code used to describe document style</p></div></a></li><li class="javascript-link-container "><a href="/en-US/docs/Web/JavaScript" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">General-purpose scripting language</p></div></a></li><li class="http-link-container "><a href="/en-US/docs/Web/HTTP" class="submenu-item "><div class="submenu-icon http"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP</div><p class="submenu-item-description">Protocol for transmitting web resources</p></div></a></li><li class="apis-link-container "><a href="/en-US/docs/Web/API" class="submenu-item "><div class="submenu-icon apis"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web APIs</div><p class="submenu-item-description">Interfaces for building web applications</p></div></a></li><li class="apis-link-container "><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Extensions</div><p class="submenu-item-description">Developing extensions for web browsers</p></div></a></li><li class=" "><a href="/en-US/docs/Web/Accessibility" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Accessibility</div><p class="submenu-item-description">Build web projects usable for all</p></div></a></li><li class="apis-link-container desktop-only "><a href="/en-US/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li></ul></li><li class="top-level-entry-container active"><button type="button" id="learn-button" class="top-level-entry menu-toggle" aria-controls="learn-menu" aria-expanded="false">Learn</button><a href="/en-US/docs/Learn_web_development" class="top-level-entry">Learn</a><ul id="learn-menu" class="submenu learn hidden inline-submenu-lg" aria-labelledby="learn-button"><li class="apis-link-container mobile-only "><a href="/en-US/docs/Learn_web_development" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="apis-link-container desktop-only "><a href="/en-US/docs/Learn_web_development" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="html-link-container "><a href="/en-US/docs/Learn_web_development/Core/Structuring_content" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Learn to structure web content with HTML</p></div></a></li><li class="css-link-container "><a href="/en-US/docs/Learn_web_development/Core/Styling_basics" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Learn to style content using CSS</p></div></a></li><li class="javascript-link-container "><a href="/en-US/docs/Learn_web_development/Core/Scripting" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">Learn to run scripts in the browser</p></div></a></li><li class=" "><a href="/en-US/docs/Learn_web_development/Core/Accessibility" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Accessibility</div><p class="submenu-item-description">Learn to make the web accessible to all</p></div></a></li></ul></li><li class="top-level-entry-container "><button type="button" id="mdn-plus-button" class="top-level-entry menu-toggle" aria-controls="mdn-plus-menu" aria-expanded="false">Plus</button><a href="/en-US/plus" class="top-level-entry">Plus</a><ul id="mdn-plus-menu" class="submenu mdn-plus hidden inline-submenu-lg" aria-labelledby="mdn-plus-button"><li class=" "><a href="/en-US/plus" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview</div><p class="submenu-item-description">A customized MDN experience</p></div></a></li><li class=" "><a href="/en-US/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li><li class=" "><a href="/en-US/plus/updates" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Updates</div><p class="submenu-item-description">All browser compatibility updates at a glance</p></div></a></li><li class=" "><a href="/en-US/plus/docs/features/overview" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Documentation</div><p class="submenu-item-description">Learn how to use MDN Plus</p></div></a></li><li class=" "><a href="/en-US/plus/docs/faq" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">FAQ</div><p class="submenu-item-description">Frequently asked questions about MDN Plus</p></div></a></li></ul></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/curriculum/">Curriculum <sup class="new">New</sup></a></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/blog/">Blog</a></li><li class="top-level-entry-container "><button type="button" id="tools-button" class="top-level-entry menu-toggle" aria-controls="tools-menu" aria-expanded="false">Tools</button><ul id="tools-menu" class="submenu tools hidden inline-submenu-lg" aria-labelledby="tools-button"><li class=" "><a href="/en-US/play" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Playground</div><p class="submenu-item-description">Write, test and share your code</p></div></a></li><li class=" "><a href="/en-US/observatory" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP Observatory</div><p class="submenu-item-description">Scan a website for free</p></div></a></li><li class=" "><a href="/en-US/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li></ul></li></ul></nav><div class="header-search"><form action="/en-US/search" class="search-form search-widget" id="top-nav-search-form" role="search"><label id="top-nav-search-label" for="top-nav-search-input" class="visually-hidden">Search MDN</label><input aria-activedescendant="" aria-autocomplete="list" aria-controls="top-nav-search-menu" aria-expanded="false" aria-labelledby="top-nav-search-label" autoComplete="off" id="top-nav-search-input" role="combobox" type="search" class="search-input-field" name="q" placeholder="   " required="" value=""/><button type="button" class="button action has-icon clear-search-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear search input</span></span></button><button type="submit" class="button action has-icon search-button"><span class="button-wrap"><span class="icon icon-search "></span><span class="visually-hidden">Search</span></span></button><div id="top-nav-search-menu" role="listbox" aria-labelledby="top-nav-search-label"></div></form></div><div class="theme-switcher-menu"><button type="button" class="button action has-icon theme-switcher-menu small" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-theme-os-default "></span>Theme</span></button></div><ul class="auth-container"><li><a href="/users/fxa/login/authenticate/?next=%2Fen-US%2Fdocs%2FLearn_web_development%2FExtensions%2FServer-side%2FDjango%2FForms" class="login-link" rel="nofollow">Log in</a></li><li><a href="/users/fxa/login/authenticate/?next=%2Fen-US%2Fdocs%2FLearn_web_development%2FExtensions%2FServer-side%2FDjango%2FForms" target="_self" rel="nofollow" class="button primary mdn-plus-subscribe-link"><span class="button-wrap">Sign up for free</span></a></li></ul></div></div></header><div class="article-actions-container"><div class="container"><button type="button" class="button action has-icon sidebar-button" aria-label="Expand sidebar" aria-expanded="false" aria-controls="sidebar-quicklinks"><span class="button-wrap"><span class="icon icon-sidebar "></span></span></button><nav class="breadcrumbs-container" aria-label="Breadcrumb"><ol typeof="BreadcrumbList" vocab="https://schema.org/" aria-label="breadcrumbs"><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn_web_development" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Learn</span></a><meta property="position" content="1"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn_web_development/Extensions" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Extension modules</span></a><meta property="position" content="2"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn_web_development/Extensions/Server-side" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Server-side website programming</span></a><meta property="position" content="3"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Django Web Framework (Python)</span></a><meta property="position" content="4"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms" class="breadcrumb-current-page" property="item" typeof="WebPage"><span property="name">Django Tutorial Part 9: Working with forms</span></a><meta property="position" content="5"/></li></ol></nav><div class="article-actions"><button type="button" class="button action has-icon article-actions-toggle" aria-label="Article actions"><span class="button-wrap"><span class="icon icon-ellipses "></span><span class="article-actions-dialog-heading">Article Actions</span></span></button><ul class="article-actions-entries"><li class="article-actions-entry"><div class="languages-switcher-menu open-on-focus-within"><button id="languages-switcher-button" type="button" class="button action small has-icon languages-switcher-menu" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-language "></span>English (US)</span></button><div class="hidden"><ul class="submenu language-menu " aria-labelledby="language-menu-button"><li class=" "><form class="submenu-item locale-redirect-setting"><div class="group"><label class="switch"><input type="checkbox" name="locale-redirect"/><span class="slider"></span><span class="label">Remember language</span></label><a href="https://github.com/orgs/mdn/discussions/739" rel="external noopener noreferrer" target="_blank" title="Enable this setting to automatically switch to this language when it&#x27;s available. (Click to learn more.)"><span class="icon icon-question-mark "></span></a></div></form></li><li class=" "><a data-locale="de" href="/de/docs/Learn_web_development/Extensions/Server-side/Django/Forms" class="button submenu-item"><span>Deutsch</span><span title="Diese Übersetzung ist Teil eines Experiments."><span class="icon icon-experimental "></span></span></a></li><li class=" "><a data-locale="es" href="/es/docs/Learn_web_development/Extensions/Server-side/Django/Forms" class="button submenu-item"><span>Español</span></a></li><li class=" "><a data-locale="fr" href="/fr/docs/Learn_web_development/Extensions/Server-side/Django/Forms" class="button submenu-item"><span>Français</span></a></li><li class=" "><a data-locale="ko" href="/ko/docs/Learn_web_development/Extensions/Server-side/Django/Forms" class="button submenu-item"><span>한국어</span></a></li><li class=" "><a data-locale="pt-BR" href="/pt-BR/docs/Learn_web_development/Extensions/Server-side/Django/Forms" class="button submenu-item"><span>Português (do Brasil)</span></a></li><li class=" "><a data-locale="ru" href="/ru/docs/Learn_web_development/Extensions/Server-side/Django/Forms" class="button submenu-item"><span>Русский</span></a></li><li class=" "><a data-locale="zh-CN" href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Forms" class="button submenu-item"><span>中文 (简体)</span></a></li><li class=" "><a data-locale="zh-TW" href="/zh-TW/docs/Learn_web_development/Extensions/Server-side/Django/Forms" class="button submenu-item"><span>正體中文 (繁體)</span></a></li></ul></div></div></li></ul></div></div></div></div><div class="main-wrapper"><div class="sidebar-container"><aside id="sidebar-quicklinks" class="sidebar"><button type="button" class="button action backdrop" aria-label="Collapse sidebar"><span class="button-wrap"></span></button><nav aria-label="Related Topics" class="sidebar-inner"><header class="sidebar-actions"><section class="sidebar-filter-container"><div class="sidebar-filter "><label id="sidebar-filter-label" class="sidebar-filter-label" for="sidebar-filter-input"><span class="icon icon-filter"></span><span class="visually-hidden">Filter sidebar</span></label><input id="sidebar-filter-input" autoComplete="off" class="sidebar-filter-input-field false" type="text" placeholder="Filter" value=""/><button type="button" class="button action has-icon clear-sidebar-filter-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear filter input</span></span></button></div></section></header><div class="sidebar-inner-nav"><div class="in-nav-toc"><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">In this article</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#overview">Overview</a></li><li class="document-toc-item "><a class="document-toc-link" href="#html_forms">HTML Forms</a></li><li class="document-toc-item "><a class="document-toc-link" href="#django_form_handling_process">Django form handling process</a></li><li class="document-toc-item "><a class="document-toc-link" href="#renew-book_form_using_a_form_and_function_view">Renew-book form using a Form and function view</a></li><li class="document-toc-item "><a class="document-toc-link" href="#modelforms">ModelForms</a></li><li class="document-toc-item "><a class="document-toc-link" href="#generic_editing_views">Generic editing views</a></li><li class="document-toc-item "><a class="document-toc-link" href="#challenge_yourself">Challenge yourself</a></li><li class="document-toc-item "><a class="document-toc-link" href="#summary">Summary</a></li><li class="document-toc-item "><a class="document-toc-link" href="#see_also">See also</a></li></ul></section></div></div><div class="sidebar-body"><ol><li class="section"><a href="/en-US/docs/Learn_web_development/Getting_started">Getting started modules</a></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Getting_started/Environment_setup">Environment setup</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Installing_software">Installing basic software</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Browsing_the_web">Browsing the web</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Code_editors">Code editors</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Dealing_with_files">Dealing with files</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Command_line">Command line crash course</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Getting_started/Your_first_website">Your first website</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Getting_started/Your_first_website/What_will_your_website_look_like">What will your website look like?</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Creating_the_content">HTML: Creating the content</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Styling_the_content">CSS: Styling the content</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Adding_interactivity">JavaScript: Adding interactivity</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Publishing_your_website">Publishing your website</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Getting_started/Web_standards">Web standards</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Getting_started/Web_standards/How_the_web_works">How the web works</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Web_standards/The_web_standards_model">The web standards model</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Web_standards/How_browsers_load_websites">How browsers load websites</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills">Soft skills</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Research_and_learning">Research and learning</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Collaboration_and_teamwork">Collaboration and teamwork</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Workflows_and_processes">Workflows and processes</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Job_interviews">Succeeding in job interviews</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn_web_development/Core">Core modules</a></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Core/Structuring_content">Structuring content with HTML</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Basic_HTML_syntax">Basic HTML syntax</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata">What's in the head? Webpage metadata</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Headings_and_paragraphs">Headings and paragraphs in HTML</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Emphasis_and_importance">Emphasis and importance</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Lists">Lists</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Structuring_documents">Structuring documents</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Advanced_text_features">Advanced text features</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Creating_links">Creating links</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Marking_up_a_letter">Challenge: Marking up a letter</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Structuring_a_page_of_content">Challenge: Structuring a page of content</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_images">HTML images</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_video_and_audio">HTML video and audio</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Mozilla_splash_page">Challenge: Mozilla splash page</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_table_basics">HTML table basics</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Table_accessibility">HTML table accessibility</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Planet_data_table">Challenge: Structuring a planet data table</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_forms">Forms and buttons in HTML</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Debugging_HTML">Debugging HTML</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Core/Styling_basics">CSS styling basics</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/What_is_CSS">What is CSS?</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Getting_started">Getting started with CSS</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Styling_a_bio_page">Challenge: Styling a biography page</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Basic_selectors">Basic CSS selectors</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Attribute_selectors">Attribute selectors</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Pseudo_classes_and_elements">Pseudo-classes and pseudo-elements</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Combinators">Combinators</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Box_model">The box model</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Handling_conflicts">Handling conflicts</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units">CSS values and units</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Sizing">Sizing items in CSS</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Backgrounds_and_borders">Backgrounds and borders</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Overflow">Overflowing content</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Images_media_forms">Images, media, and form elements</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Tables">Styling tables</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Debugging_CSS">Debugging CSS</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Fundamental_CSS_comprehension">Challenge: Fundamental CSS comprehension</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Fancy_letterheaded_paper">Challenge: Creating fancy letterheaded paper</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Styling_basics/Cool-looking_box">Challenge: A cool-looking box</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Core/Text_styling">CSS text styling</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Core/Text_styling/Fundamentals">Fundamental text and font styling</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Text_styling/Styling_lists">Styling lists</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Text_styling/Styling_links">Styling links</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Text_styling/Web_fonts">Web fonts</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Text_styling/Typesetting_a_homepage">Challenge: Typesetting a community school homepage</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Core/CSS_layout">CSS layout</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Core/CSS_layout/Introduction">Introduction to CSS layout</a></li><li><a href="/en-US/docs/Learn_web_development/Core/CSS_layout/Floats">Floats</a></li><li><a href="/en-US/docs/Learn_web_development/Core/CSS_layout/Positioning">Positioning</a></li><li><a href="/en-US/docs/Learn_web_development/Core/CSS_layout/Flexbox">Flexbox</a></li><li><a href="/en-US/docs/Learn_web_development/Core/CSS_layout/Grids">CSS grid layout</a></li><li><a href="/en-US/docs/Learn_web_development/Core/CSS_layout/Responsive_Design">Responsive design</a></li><li><a href="/en-US/docs/Learn_web_development/Core/CSS_layout/Media_queries">Media query fundamentals</a></li><li><a href="/en-US/docs/Learn_web_development/Core/CSS_layout/Fundamental_Layout_Comprehension">Challenge: Fundamental layout comprehension</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Core/Scripting">Dynamic scripting with JavaScript</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/What_is_JavaScript">What is JavaScript?</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/A_first_splash">A first splash into JavaScript</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/What_went_wrong">What went wrong? Troubleshooting JavaScript</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Variables">Storing the information you need — Variables</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Math">Basic math in JavaScript — numbers and operators</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Strings">Handling text — strings in JavaScript</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Useful_string_methods">Useful string methods</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Arrays">Arrays</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Silly_story_generator">Challenge: Silly story generator</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Conditionals">Making decisions in your code — conditionals</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Loops">Looping code</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Functions">Functions — reusable blocks of code</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Build_your_own_function">Build your own function</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Return_values">Function return values</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Events">Introduction to events</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Event_bubbling">Event bubbling</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Image_gallery">Challenge: Image gallery</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Object_basics">JavaScript object basics</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/DOM_scripting">DOM scripting introduction</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Network_requests">Making network requests with JavaScript</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/JSON">Working with JSON</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Scripting/Debugging_JavaScript">Debugging JavaScript and handling errors</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries">JavaScript frameworks and libraries</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/Introduction">Introduction to client-side frameworks</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/Main_features">Framework main features</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_getting_started">Getting started with React</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_todo_list_beginning">Beginning our React todo list</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_components">Componentizing our React app</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_interactivity_events_state">React interactivity: Events and state</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_interactivity_filtering_conditional_rendering">React interactivity: Editing, filtering, conditional rendering</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_accessibility">Accessibility in React</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_resources">React resources</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Core/Accessibility">Accessibility</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Core/Accessibility/What_is_accessibility">What is accessibility?</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Accessibility/Tooling">Accessibility tooling and assistive technology</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Accessibility/HTML">HTML: A good basis for accessibility</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Accessibility/CSS_and_JavaScript">CSS and JavaScript accessibility best practices</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Accessibility/WAI-ARIA_basics">WAI-ARIA basics</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Accessibility/Multimedia">Accessible multimedia</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Accessibility/Mobile">Mobile accessibility</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Accessibility/Accessibility_troubleshooting">Challenge: Accessibility troubleshooting</a></li></ol></details></li><li><a href="/en-US/docs/Learn_web_development/Core/Design_for_developers">Design for developers</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Version_control">Version control</a></li><li class="section"><a href="/en-US/docs/Learn_web_development/Extensions">Extension modules</a></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects">Advanced JavaScript objects</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes">Object prototypes</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object-oriented_programming">Object-oriented programming</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Classes_in_JavaScript">Classes in JavaScript</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_building_practice">Object building practice</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Adding_bouncing_balls_features">Challenge: Adding features to our bouncing balls demo</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_APIs">Client-side web APIs</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Introduction">Introduction to web APIs</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Video_and_audio_APIs">Video and Audio APIs</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics">Drawing graphics</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Client-side_storage">Client-side storage</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Third_party_APIs">Third-party APIs</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Extensions/Async_JS">Asynchronous JavaScript</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Async_JS/Introducing">Introducing asynchronous JavaScript</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Async_JS/Promises">How to use promises</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Async_JS/Implementing_a_promise-based_API">How to implement a promise-based API</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Async_JS/Introducing_workers">Introducing workers</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Async_JS/Sequencing_animations">Challenge: Sequencing animations</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Extensions/Forms">Web forms</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/Your_first_form">Your first form</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/How_to_structure_a_web_form">How to structure a web form</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/Basic_native_form_controls">Basic native form controls</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/HTML5_input_types">The HTML5 input types</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/Other_form_controls">Other form controls</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/Styling_web_forms">Styling web forms</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/Advanced_form_styling">Advanced form styling</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/UI_pseudo-classes">UI pseudo-classes</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/Form_validation">Client-side form validation</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Forms/Sending_and_retrieving_form_data">Sending form data</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_tools">Understanding client-side tools</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_tools/Overview">Client-side tooling overview</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_tools/Package_management">Package management basics</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_tools/Introducing_complete_toolchain">Introducing a complete toolchain</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Client-side_tools/Deployment">Deploying our app</a></li></ol></details></li><li class="toggle"><details open=""><summary><a href="/en-US/docs/Learn_web_development/Extensions/Server-side">Server-side websites</a></summary><ol><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps">Server-side first steps</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Introduction">Introduction to the server side</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Client-Server_overview">Client-Server Overview</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Web_frameworks">Server-side web frameworks</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Website_security">Website security</a></li></ol></details></li><li class="toggle"><details open=""><summary><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django">Django web framework (Python)</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Introduction">Django introduction</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/development_environment">Setting up a Django development environment</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li><li><em><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms" aria-current="page">Django Tutorial Part 9: Working with forms</a></em></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/web_application_security">Django web application security</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/django_assessment_blog">Assessment: DIY Django mini blog</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs">Express web framework (Node.js)</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/development_environment">Setting up a Node development environment</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li></ol></details></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Extensions/Performance">Web performance</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/why_web_performance">The "why" of web performance</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/What_is_web_performance">What is web performance?</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/Perceived_performance">Perceived performance</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/Measuring_performance">Measuring performance</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/Multimedia">Multimedia: Images</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/video">Multimedia: video</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/JavaScript">JavaScript performance optimization</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/HTML">HTML performance optimization</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/CSS">CSS performance optimization</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Performance/business_case_for_performance">The business case for web performance</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Extensions/Testing">Testing</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Extensions/Testing/Introduction">Introduction to cross-browser testing</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Testing/Testing_strategies">Strategies for carrying out testing</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Testing/HTML_and_CSS">Handling common HTML and CSS problems</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Testing/Feature_detection">Implementing feature detection</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Testing/Automated_testing">Introduction to automated testing</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Testing/Your_own_automation_environment">Setting up your own test automation environment</a></li></ol></details></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Transform_animate">Transform and animate CSS</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Security_privacy">Security and privacy</a></li><li class="section">Further resources</li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Howto">How to solve common problems</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Howto/Solve_HTML_problems">Solve common HTML problems</a></li><li><a href="/en-US/docs/Learn_web_development/Howto/Solve_CSS_problems">Solve common CSS problems</a></li><li><a href="/en-US/docs/Learn_web_development/Howto/Solve_JavaScript_problems">Solve common JavaScript problems</a></li><li><a href="/en-US/docs/Learn_web_development/Howto/Web_mechanics">Web mechanics</a></li><li><a href="/en-US/docs/Learn_web_development/Howto/Tools_and_setup">Tools and setup</a></li><li><a href="/en-US/docs/Learn_web_development/Howto/Design_and_accessibility">Design and accessibility</a></li></ol></details></li><li><a href="/en-US/docs/Learn_web_development/About">About</a></li><li><a href="/en-US/docs/Learn_web_development/Educators">Resources for educators</a></li><li><a href="/en-US/docs/Learn_web_development/Changelog">Changelog</a></li></ol></div></div><section class="place side"></section></nav></aside><div class="toc-container"><aside class="toc"><nav><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">In this article</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#overview">Overview</a></li><li class="document-toc-item "><a class="document-toc-link" href="#html_forms">HTML Forms</a></li><li class="document-toc-item "><a class="document-toc-link" href="#django_form_handling_process">Django form handling process</a></li><li class="document-toc-item "><a class="document-toc-link" href="#renew-book_form_using_a_form_and_function_view">Renew-book form using a Form and function view</a></li><li class="document-toc-item "><a class="document-toc-link" href="#modelforms">ModelForms</a></li><li class="document-toc-item "><a class="document-toc-link" href="#generic_editing_views">Generic editing views</a></li><li class="document-toc-item "><a class="document-toc-link" href="#challenge_yourself">Challenge yourself</a></li><li class="document-toc-item "><a class="document-toc-link" href="#summary">Summary</a></li><li class="document-toc-item "><a class="document-toc-link" href="#see_also">See also</a></li></ul></section></div></nav></aside><section class="place side"></section></div></div><main id="content" class="main-content "><article class="main-page-content" lang="en-US"><header><h1>Django Tutorial Part 9: Working with forms</h1></header><div class="section-content"><ul class="prev-next"><li><a class="button secondary" href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Sessions"><span class="button-wrap"> Previous </span></a></li><li><a class="button secondary" href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django"><span class="button-wrap"> Overview: Django Web Framework (Python)</span></a></li><li><a class="button secondary" href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Testing"><span class="button-wrap"> Next </span></a></li></ul> <p>In this tutorial, we'll show you how to work with HTML Forms in Django, and, in particular, the easiest way to write forms to create, update, and delete model instances. As part of this demonstration, we'll extend the <a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> website so that librarians can renew books, and create, update, and delete authors using our own forms (rather than using the admin application).</p> <figure class="table-container"><table> <tbody> <tr> <th scope="row">Prerequisites:</th> <td> Complete all previous tutorial topics, including <a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a>. </td> </tr> <tr> <th scope="row">Objective:</th> <td> To understand how to write forms to get information from users and update the database. To understand how the generic class-based editing views can vastly simplify creating forms for working with a single model. </td> </tr> </tbody> </table></figure></div><section aria-labelledby="overview"><h2 id="overview"><a href="#overview">Overview</a></h2><div class="section-content"><p>An <a href="/en-US/docs/Learn_web_development/Extensions/Forms">HTML Form</a> is a group of one or more fields/widgets on a web page, which can be used to collect information from users for submission to a server. Forms are a flexible mechanism for collecting user input because there are suitable widgets for entering many different types of data, including text boxes, checkboxes, radio buttons, date pickers and so on. Forms are also a relatively secure way of sharing data with the server, as they allow us to send data in <code>POST</code> requests with cross-site request forgery protection.</p> <p>While we haven't created any forms in this tutorial so far, we've already encountered them in the Django Admin site — for example, the screenshot below shows a form for editing one of our <a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Models">Book</a> models, comprised of a number of selection lists and text editors.</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/admin_book_add.png" alt="Admin Site - Book Add" width="841" height="780" loading="lazy"></p> <p>Working with forms can be complicated! Developers need to write HTML for the form, validate and properly sanitize entered data on the server (and possibly also in the browser), repost the form with error messages to inform users of any invalid fields, handle the data when it has successfully been submitted, and finally respond to the user in some way to indicate success. <em>Django Forms</em> take a lot of the work out of all these steps, by providing a framework that lets you define forms and their fields programmatically, and then use these objects to both generate the form HTML code and handle much of the validation and user interaction.</p> <p>In this tutorial, we're going to show you a few of the ways you can create and work with forms, and in particular, how the generic editing views can significantly reduce the amount of work you need to do to create forms to manipulate your models. Along the way, we'll extend our <em>LocalLibrary</em> application by adding a form to allow librarians to renew library books, and we'll create pages to create, edit and delete books and authors (reproducing a basic version of the form shown above for editing books).</p></div></section><section aria-labelledby="html_forms"><h2 id="html_forms"><a href="#html_forms">HTML Forms</a></h2><div class="section-content"><p>First, a brief overview of <a href="/en-US/docs/Learn_web_development/Extensions/Forms">HTML Forms</a>. Consider a simple HTML form, with a single text field for entering the name of some "team", and its associated label:</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/form_example_name_field.png" alt="Simple name field example in HTML form" width="399" height="44" loading="lazy"></p> <p>The form is defined in HTML as a collection of elements inside <code>&lt;form&gt;…&lt;/form&gt;</code> tags, containing at least one <code>input</code> element of <code>type="submit"</code>.</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code>&lt;form action="/team_name_url/" method="post"&gt; &lt;label for="team_name"&gt;Enter name: &lt;/label&gt; &lt;input id="team_name" type="text" name="name_field" value="Default name for team." /&gt; &lt;input type="submit" value="OK" /&gt; &lt;/form&gt; </code></pre></div> <p>While here we just have one text field for entering the team name, a form <em>may</em> have any number of other input elements and their associated labels. The field's <code>type</code> attribute defines what sort of widget will be displayed. The <code>name</code> and <code>id</code> of the field are used to identify the field in JavaScript/CSS/HTML, while <code>value</code> defines the initial value for the field when it is first displayed. The matching team label is specified using the <code>label</code> tag (see "Enter name" above), with a <code>for</code> field containing the <code>id</code> value of the associated <code>input</code>.</p> <p>The <code>submit</code> input will be displayed as a button by default. This can be pressed to upload the data in all the other input elements in the form to the server (in this case, just the <code>team_name</code> field). The form attributes define the HTTP <code>method</code> used to send the data and the destination of the data on the server (<code>action</code>):</p> <ul> <li> <p><code>action</code>: The resource/URL where data is to be sent for processing when the form is submitted. If this is not set (or set to an empty string), then the form will be submitted back to the current page URL.</p> </li> <li> <p><code>method</code>: The HTTP method used to send the data: <em>post</em> or <em>get</em>.</p> <ul> <li>The <code>POST</code> method should always be used if the data is going to result in a change to the server's database, because it can be made more resistant to cross-site forgery request attacks.</li> <li>The <code>GET</code> method should only be used for forms that don't change user data (for example, a search form). It is recommended for when you want to be able to bookmark or share the URL.</li> </ul> </li> </ul> <p>The role of the server is first to render the initial form state — either containing blank fields or pre-populated with initial values. After the user presses the submit button, the server will receive the form data with values from the web browser and must validate the information. If the form contains invalid data, the server should display the form again, this time with user-entered data in "valid" fields and messages to describe the problem for the invalid fields. Once the server gets a request with all valid form data, it can perform an appropriate action (such as: saving the data, returning the result of a search, uploading a file, etc.) and then notify the user.</p> <p>As you can imagine, creating the HTML, validating the returned data, re-displaying the entered data with error reports if needed, and performing the desired operation on valid data can all take quite a lot of effort to "get right". Django makes this a lot easier by taking away some of the heavy lifting and repetitive code!</p></div></section><section aria-labelledby="django_form_handling_process"><h2 id="django_form_handling_process"><a href="#django_form_handling_process">Django form handling process</a></h2><div class="section-content"><p>Django's form handling uses all of the same techniques that we learned about in previous tutorials (for displaying information about our models): the view gets a request, performs any actions required including reading data from the models, then generates and returns an HTML page (from a template, into which we pass a <em>context</em> containing the data to be displayed). What makes things more complicated is that the server also needs to be able to process data provided by the user, and redisplay the page if there are any errors.</p> <p>A process flowchart of how Django handles form requests is shown below, starting with a request for a page containing a form (shown in green).</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/form_handling_-_standard.png" alt="Updated form handling process doc." width="1180" height="840" loading="lazy"></p> <p>Based on the diagram above, the main things that Django's form handling does are:</p> <ol> <li> <p>Display the default form the first time it is requested by the user.</p> <ul> <li>The form may contain blank fields if you're creating a new record, or it may be pre-populated with initial values (for example, if you are changing a record, or have useful default initial values).</li> <li>The form is referred to as <em>unbound</em> at this point, because it isn't associated with any user-entered data (though it may have initial values).</li> </ul> </li> <li> <p>Receive data from a submit request and bind it to the form.</p> <ul> <li>Binding data to the form means that the user-entered data and any errors are available when we need to redisplay the form.</li> </ul> </li> <li> <p>Clean and validate the data.</p> <ul> <li>Cleaning the data performs sanitization of the input fields, such as removing invalid characters that might be used to send malicious content to the server, and converts them into consistent Python types.</li> <li>Validation checks that the values are appropriate for the field (for example, that they are in the right date range, aren't too short or too long, etc.)</li> </ul> </li> <li> <p>If any data is invalid, re-display the form, this time with any user populated values and error messages for the problem fields.</p> </li> <li> <p>If all data is valid, perform required actions (such as save the data, send an email, return the result of a search, upload a file, and so on).</p> </li> <li> <p>Once all actions are complete, redirect the user to another page.</p> </li> </ol> <p>Django provides a number of tools and approaches to help you with the tasks detailed above. The most fundamental is the <code>Form</code> class, which simplifies both generation of form HTML and data cleaning/validation. In the next section, we describe how forms work using the practical example of a page to allow librarians to renew books.</p> <div class="notecard note"> <p><strong>Note:</strong> Understanding how <code>Form</code> is used will help you when we discuss Django's more "high level" form framework classes.</p> </div></div></section><section aria-labelledby="renew-book_form_using_a_form_and_function_view"><h2 id="renew-book_form_using_a_form_and_function_view"><a href="#renew-book_form_using_a_form_and_function_view">Renew-book form using a Form and function view</a></h2><div class="section-content"><p>Next, we're going to add a page to allow librarians to renew borrowed books. To do this we'll create a form that allows users to enter a date value. We'll seed the field with an initial value 3 weeks from the current date (the normal borrowing period), and add some validation to ensure that the librarian can't enter a date in the past or a date too far in the future. When a valid date has been entered, we'll write it to the current record's <code>BookInstance.due_back</code> field.</p> <p>The example will use a function-based view and a <code>Form</code> class. The following sections explain how forms work, and the changes you need to make to our ongoing <em>LocalLibrary</em> project.</p></div></section><section aria-labelledby="form"><h3 id="form"><a href="#form">Form</a></h3><div class="section-content"><p>The <code>Form</code> class is the heart of Django's form handling system. It specifies the fields in the form, their layout, display widgets, labels, initial values, valid values, and (once validated) the error messages associated with invalid fields. The class also provides methods for rendering itself in templates using predefined formats (tables, lists, etc.) or for getting the value of any element (enabling fine-grained manual rendering).</p> <h4 id="declaring_a_form">Declaring a Form</h4> <p>The declaration syntax for a <code>Form</code> is very similar to that for declaring a <code>Model</code>, and shares the same field types (and some similar parameters). This makes sense because in both cases we need to ensure that each field handles the right types of data, is constrained to valid data, and has a description for display/documentation.</p> <p>Form data is stored in an application's forms.py file, inside the application directory. Create and open the file <strong>django-locallibrary-tutorial/catalog/forms.py</strong>. To create a <code>Form</code>, we import the <code>forms</code> library, derive from the <code>Form</code> class, and declare the form's fields. A very basic form class for our library book renewal form is shown below — add this to your new file:</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>from django import forms class RenewBookForm(forms.Form): renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).") </code></pre></div> <h4 id="form_fields">Form fields</h4> <p>In this case, we have a single <a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#datefield" class="external" target="_blank"><code>DateField</code></a> for entering the renewal date that will render in HTML with a blank value, the default label "<em>Renewal date:</em>", and some helpful usage text: "<em>Enter a date between now and 4 weeks (default 3 weeks).</em>" As none of the other optional arguments are specified the field will accept dates using the <a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#django.forms.DateField.input_formats" class="external" target="_blank">input_formats</a>: YYYY-MM-DD (2024-11-06), MM/DD/YYYY (02/26/2024), MM/DD/YY (10/25/24), and will be rendered using the default <a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#widget" class="external" target="_blank">widget</a>: <a href="https://docs.djangoproject.com/en/5.0/ref/forms/widgets/#django.forms.DateInput" class="external" target="_blank">DateInput</a>.</p> <p>There are many other types of form fields, which you will largely recognize from their similarity to the equivalent model field classes:</p> <ul> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#booleanfield" class="external" target="_blank"><code>BooleanField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#charfield" class="external" target="_blank"><code>CharField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#choicefield" class="external" target="_blank"><code>ChoiceField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#typedchoicefield" class="external" target="_blank"><code>TypedChoiceField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#datefield" class="external" target="_blank"><code>DateField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#datetimefield" class="external" target="_blank"><code>DateTimeField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#decimalfield" class="external" target="_blank"><code>DecimalField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#durationfield" class="external" target="_blank"><code>DurationField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#emailfield" class="external" target="_blank"><code>EmailField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#filefield" class="external" target="_blank"><code>FileField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#filepathfield" class="external" target="_blank"><code>FilePathField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#floatfield" class="external" target="_blank"><code>FloatField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#imagefield" class="external" target="_blank"><code>ImageField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#integerfield" class="external" target="_blank"><code>IntegerField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#genericipaddressfield" class="external" target="_blank"><code>GenericIPAddressField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#multiplechoicefield" class="external" target="_blank"><code>MultipleChoiceField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#typedmultiplechoicefield" class="external" target="_blank"><code>TypedMultipleChoiceField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#nullbooleanfield" class="external" target="_blank"><code>NullBooleanField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#regexfield" class="external" target="_blank"><code>RegexField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#slugfield" class="external" target="_blank"><code>SlugField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#timefield" class="external" target="_blank"><code>TimeField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#urlfield" class="external" target="_blank"><code>URLField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#uuidfield" class="external" target="_blank"><code>UUIDField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#combofield" class="external" target="_blank"><code>ComboField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#multivaluefield" class="external" target="_blank"><code>MultiValueField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#splitdatetimefield" class="external" target="_blank"><code>SplitDateTimeField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#modelmultiplechoicefield" class="external" target="_blank"><code>ModelMultipleChoiceField</code></a></li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#modelchoicefield" class="external" target="_blank"><code>ModelChoiceField</code></a></li> </ul> <p>The arguments that are common to most fields are listed below (these have sensible default values):</p> <ul> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#required" class="external" target="_blank"><code>required</code></a>: If <code>True</code>, the field may not be left blank or given a <code>None</code> value. Fields are required by default, so you would set <code>required=False</code> to allow blank values in the form.</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#label" class="external" target="_blank"><code>label</code></a>: The label to use when rendering the field in HTML. If a <a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#label" class="external" target="_blank">label</a> is not specified, Django will create one from the field name by capitalizing the first letter and replacing underscores with spaces (e.g. <em>Renewal date</em>).</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#label-suffix" class="external" target="_blank"><code>label_suffix</code></a>: By default, a colon is displayed after the label (e.g. Renewal date​<strong>:</strong>). This argument allows you to specify a different suffix containing other character(s).</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#initial" class="external" target="_blank"><code>initial</code></a>: The initial value for the field when the form is displayed.</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#widget" class="external" target="_blank"><code>widget</code></a>: The display widget to use.</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#help-text" class="external" target="_blank"><code>help_text</code></a> (as seen in the example above): Additional text that can be displayed in forms to explain how to use the field.</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#error-messages" class="external" target="_blank"><code>error_messages</code></a>: A list of error messages for the field. You can override these with your own messages if needed.</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#validators" class="external" target="_blank"><code>validators</code></a>: A list of functions that will be called on the field when it is validated.</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#localize" class="external" target="_blank"><code>localize</code></a>: Enables the localization of form data input (see link for more information).</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/#disabled" class="external" target="_blank"><code>disabled</code></a>: The field is displayed but its value cannot be edited if this is <code>True</code>. The default is <code>False</code>.</li> </ul> <h4 id="validation">Validation</h4> <p>Django provides numerous places where you can validate your data. The easiest way to validate a single field is to override the method <code>clean_&lt;field_name&gt;()</code> for the field you want to check. So for example, we can validate that entered <code>renewal_date</code> values are between now and 4 weeks by implementing <code>clean_renewal_date()</code> as shown below.</p> <p>Update your forms.py file so it looks like this:</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>import datetime from django import forms from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ class RenewBookForm(forms.Form): renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).") def clean_renewal_date(self): data = self.cleaned_data['renewal_date'] # Check if a date is not in the past. if data &lt; datetime.date.today(): raise ValidationError(_('Invalid date - renewal in past')) # Check if a date is in the allowed range (+4 weeks from today). if data &gt; datetime.date.today() + datetime.timedelta(weeks=4): raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead')) # Remember to always return the cleaned data. return data </code></pre></div> <p>There are two important things to note. The first is that we get our data using <code>self.cleaned_data['renewal_date']</code> and that we return this data whether or not we change it at the end of the function. This step gets us the data "cleaned" and sanitized of potentially unsafe input using the default validators, and converted into the correct standard type for the data (in this case a Python <code>datetime.datetime</code> object).</p> <p>The second point is that if a value falls outside our range we raise a <code>ValidationError</code>, specifying the error text that we want to display in the form if an invalid value is entered. The example above also wraps this text in one of Django's <a href="https://docs.djangoproject.com/en/5.0/topics/i18n/translation/" class="external" target="_blank">translation functions</a>, <code>gettext_lazy()</code> (imported as <code>_()</code>), which is good practice if you want to translate your site later.</p> <div class="notecard note"> <p><strong>Note:</strong> There are numerous other methods and examples for validating forms in <a href="https://docs.djangoproject.com/en/5.0/ref/forms/validation/" class="external" target="_blank">Form and field validation</a> (Django docs). For example, in cases where you have multiple fields that depend on each other, you can override the <a href="https://docs.djangoproject.com/en/5.0/ref/forms/api/#django.forms.Form.clean" class="external" target="_blank">Form.clean()</a> function and again raise a <code>ValidationError</code>.</p> </div> <p>That's all we need for the form in this example!</p></div></section><section aria-labelledby="url_configuration"><h3 id="url_configuration"><a href="#url_configuration">URL configuration</a></h3><div class="section-content"><p>Before we create our view, let's add a URL configuration for the <em>renew-books</em> page. Copy the following configuration to the bottom of <strong>django-locallibrary-tutorial/catalog/urls.py</strong>:</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>urlpatterns += [ path('book/&lt;uuid:pk&gt;/renew/', views.renew_book_librarian, name='renew-book-librarian'), ] </code></pre></div> <p>The URL configuration will redirect URLs with the format <strong>/catalog/book/<em>&lt;bookinstance_id&gt;</em>/renew/</strong> to the function named <code>renew_book_librarian()</code> in <strong>views.py</strong>, and send the <code>BookInstance</code> id as the parameter named <code>pk</code>. The pattern only matches if <code>pk</code> is a correctly formatted <code>uuid</code>.</p> <div class="notecard note"> <p><strong>Note:</strong> We can name our captured URL data anything we like, because we have complete control over the view function (we're not using a generic detail view class that expects parameters with a certain name). However, <code>pk</code> short for "primary key", is a reasonable convention to use!</p> </div></div></section><section aria-labelledby="view"><h3 id="view"><a href="#view">View</a></h3><div class="section-content"><p>As discussed in the <a href="#django_form_handling_process">Django form handling process</a> above, the view has to render the default form when it is first called and then either re-render it with error messages if the data is invalid, or process the data and redirect to a new page if the data is valid. In order to perform these different actions, the view has to be able to know whether it is being called for the first time to render the default form, or a subsequent time to validate data.</p> <p>For forms that use a <code>POST</code> request to submit information to the server, the most common pattern is for the view to test against the <code>POST</code> request type (<code>if request.method == 'POST':</code>) to identify form validation requests and <code>GET</code> (using an <code>else</code> condition) to identify the initial form creation request. If you want to submit your data using a <code>GET</code> request, then a typical approach for identifying whether this is the first or subsequent view invocation is to read the form data (e.g. to read a hidden value in the form).</p> <p>The book renewal process will be writing to our database, so, by convention, we use the <code>POST</code> request approach. The code fragment below shows the (very standard) pattern for this sort of function view.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>import datetime from django.shortcuts import render, get_object_or_404 from django.http import HttpResponseRedirect from django.urls import reverse from catalog.forms import RenewBookForm def renew_book_librarian(request, pk): book_instance = get_object_or_404(BookInstance, pk=pk) # If this is a POST request then process the Form data if request.method == 'POST': # Create a form instance and populate it with data from the request (binding): form = RenewBookForm(request.POST) # Check if the form is valid: if form.is_valid(): # process the data in form.cleaned_data as required (here we just write it to the model due_back field) book_instance.due_back = form.cleaned_data['renewal_date'] book_instance.save() # redirect to a new URL: return HttpResponseRedirect(reverse('all-borrowed')) # If this is a GET (or any other method) create the default form. else: proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3) form = RenewBookForm(initial={'renewal_date': proposed_renewal_date}) context = { 'form': form, 'book_instance': book_instance, } return render(request, 'catalog/book_renew_librarian.html', context) </code></pre></div> <p>First, we import our form (<code>RenewBookForm</code>) and a number of other useful objects/methods used in the body of the view function:</p> <ul> <li><a href="https://docs.djangoproject.com/en/5.0/topics/http/shortcuts/#get-object-or-404" class="external" target="_blank"><code>get_object_or_404()</code></a>: Returns a specified object from a model based on its primary key value, and raises an <code>Http404</code> exception (not found) if the record does not exist.</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/request-response/#django.http.HttpResponseRedirect" class="external" target="_blank"><code>HttpResponseRedirect</code></a>: This creates a redirect to a specified URL (HTTP status code 302).</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/urlresolvers/#django.urls.reverse" class="external" target="_blank"><code>reverse()</code></a>: This generates a URL from a URL configuration name and a set of arguments. It is the Python equivalent of the <code>url</code> tag that we've been using in our templates.</li> <li><a href="https://docs.python.org/3/library/datetime.html" class="external" target="_blank"><code>datetime</code></a>: A Python library for manipulating dates and times.</li> </ul> <p>In the view, we first use the <code>pk</code> argument in <code>get_object_or_404()</code> to get the current <code>BookInstance</code> (if this does not exist, the view will immediately exit and the page will display a "not found" error). If this is <em>not</em> a <code>POST</code> request (handled by the <code>else</code> clause) then we create the default form passing in an <code>initial</code> value for the <code>renewal_date</code> field, 3 weeks from the current date.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>book_instance = get_object_or_404(BookInstance, pk=pk) # If this is a GET (or any other method) create the default form else: proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3) form = RenewBookForm(initial={'renewal_date': proposed_renewal_date}) context = { 'form': form, 'book_instance': book_instance, } return render(request, 'catalog/book_renew_librarian.html', context) </code></pre></div> <p>After creating the form, we call <code>render()</code> to create the HTML page, specifying the template and a context that contains our form. In this case, the context also contains our <code>BookInstance</code>, which we'll use in the template to provide information about the book we're renewing.</p> <p>However, if this is a <code>POST</code> request, then we create our <code>form</code> object and populate it with data from the request. This process is called "binding" and allows us to validate the form.</p> <p>We then check if the form is valid, which runs all the validation code on all of the fields — including both the generic code to check that our date field is actually a valid date and our specific form's <code>clean_renewal_date()</code> function to check the date is in the right range.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>book_instance = get_object_or_404(BookInstance, pk=pk) # If this is a POST request then process the Form data if request.method == 'POST': # Create a form instance and populate it with data from the request (binding): form = RenewBookForm(request.POST) # Check if the form is valid: if form.is_valid(): # process the data in form.cleaned_data as required (here we just write it to the model due_back field) book_instance.due_back = form.cleaned_data['renewal_date'] book_instance.save() # redirect to a new URL: return HttpResponseRedirect(reverse('all-borrowed')) context = { 'form': form, 'book_instance': book_instance, } return render(request, 'catalog/book_renew_librarian.html', context) </code></pre></div> <p>If the form is not valid we call <code>render()</code> again, but this time the form value passed in the context will include error messages.</p> <p>If the form is valid, then we can start to use the data, accessing it through the <code>form.cleaned_data</code> attribute (e.g. <code>data = form.cleaned_data['renewal_date']</code>). Here, we just save the data into the <code>due_back</code> value of the associated <code>BookInstance</code> object.</p> <div class="notecard warning"> <p><strong>Warning:</strong> While you can also access the form data directly through the request (for example, <code>request.POST['renewal_date']</code> or <code>request.GET['renewal_date']</code> if using a GET request), this is NOT recommended. The cleaned data is sanitized, validated, and converted into Python-friendly types.</p> </div> <p>The final step in the form-handling part of the view is to redirect to another page, usually a "success" page. In this case, we use <code>HttpResponseRedirect</code> and <code>reverse()</code> to redirect to the view named <code>'all-borrowed'</code> (this was created as the "challenge" in <a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Authentication#challenge_yourself">Django Tutorial Part 8: User authentication and permissions</a>). If you didn't create that page consider redirecting to the home page at URL <code>/</code>).</p> <p>That's everything needed for the form handling itself, but we still need to restrict access to the view to just logged-in librarians who have permission to renew books. We use <code>@login_required</code> to require that the user is logged in, and the <code>@permission_required</code> function decorator with our existing <code>can_mark_returned</code> permission to allow access (decorators are processed in order). Note that we probably should have created a new permission setting in <code>BookInstance</code> (<code>can_renew</code>), but we will reuse the existing one to keep the example simple.</p> <p>The final view is therefore as shown below. Please copy this into the bottom of <strong>django-locallibrary-tutorial/catalog/views.py</strong>.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>import datetime from django.contrib.auth.decorators import login_required, permission_required from django.shortcuts import get_object_or_404 from django.http import HttpResponseRedirect from django.urls import reverse from catalog.forms import RenewBookForm @login_required @permission_required('catalog.can_mark_returned', raise_exception=True) def renew_book_librarian(request, pk): """View function for renewing a specific BookInstance by librarian.""" book_instance = get_object_or_404(BookInstance, pk=pk) # If this is a POST request then process the Form data if request.method == 'POST': # Create a form instance and populate it with data from the request (binding): form = RenewBookForm(request.POST) # Check if the form is valid: if form.is_valid(): # process the data in form.cleaned_data as required (here we just write it to the model due_back field) book_instance.due_back = form.cleaned_data['renewal_date'] book_instance.save() # redirect to a new URL: return HttpResponseRedirect(reverse('all-borrowed')) # If this is a GET (or any other method) create the default form. else: proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3) form = RenewBookForm(initial={'renewal_date': proposed_renewal_date}) context = { 'form': form, 'book_instance': book_instance, } return render(request, 'catalog/book_renew_librarian.html', context) </code></pre></div></div></section><section aria-labelledby="the_template"><h3 id="the_template"><a href="#the_template">The template</a></h3><div class="section-content"><p>Create the template referenced in the view (<strong>/catalog/templates/catalog/book_renew_librarian.html</strong>) and copy the code below into it:</p> <div class="code-example"><div class="example-header"><span class="language-name">django</span></div><pre class="brush: django notranslate"><code>{% extends "base_generic.html" %} {% block content %} &lt;h1&gt;Renew: {{ book_instance.book.title }}&lt;/h1&gt; &lt;p&gt;Borrower: {{ book_instance.borrower }}&lt;/p&gt; &lt;p {% if book_instance.is_overdue %} class="text-danger"{% endif %} &gt;Due date: {{ book_instance.due_back }}&lt;/p&gt; &lt;form action="" method="post"&gt; {% csrf_token %} &lt;table&gt; {{ form.as_table }} &lt;/table&gt; &lt;input type="submit" value="Submit"&gt; &lt;/form&gt; {% endblock %} </code></pre></div> <p>Most of this will be completely familiar from previous tutorials.</p> <p>We extend the base template and then redefine the content block. We are able to reference <code>{{ book_instance }}</code> (and its variables) because it was passed into the context object in the <code>render()</code> function, and we use these to list the book title, borrower, and the original due date.</p> <p>The form code is relatively simple. First, we declare the <code>form</code> tags, specifying where the form is to be submitted (<code>action</code>) and the <code>method</code> for submitting the data (in this case a <code>POST</code>) — if you recall the <a href="#html_forms">HTML Forms</a> overview at the top of the page, an empty <code>action</code> as shown, means that the form data will be posted back to the current URL of the page (which is what we want). Inside the tags, we define the <code>submit</code> input, which a user can press to submit the data. The <code>{% csrf_token %}</code> added just inside the form tags is part of Django's cross-site forgery protection.</p> <div class="notecard note"> <p><strong>Note:</strong> Add the <code>{% csrf_token %}</code> to every Django template you create that uses <code>POST</code> to submit data. This will reduce the chance of forms being hijacked by malicious users.</p> </div> <p>All that's left is the <code>{{ form }}</code> template variable, which we passed to the template in the context dictionary. Perhaps unsurprisingly, when used as shown this provides the default rendering of all the form fields, including their labels, widgets, and help text — the rendering is as shown below:</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code>&lt;tr&gt; &lt;th&gt;&lt;label for="id_renewal_date"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt; &lt;td&gt; &lt;input id="id_renewal_date" name="renewal_date" type="text" value="2023-11-08" required /&gt; &lt;br /&gt; &lt;span class="helptext"&gt; Enter date between now and 4 weeks (default 3 weeks). &lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; </code></pre></div> <div class="notecard note"> <p><strong>Note:</strong> It is perhaps not obvious because we only have one field, but, by default, every field is defined in its own table row. This same rendering is provided if you reference the template variable <code>{{ form.as_table }}</code>.</p> </div> <p>If you were to enter an invalid date, you'd additionally get a list of the errors rendered on the page (see <code>errorlist</code> below).</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code>&lt;tr&gt; &lt;th&gt;&lt;label for="id_renewal_date"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt; &lt;td&gt; &lt;ul class="errorlist"&gt; &lt;li&gt;Invalid date - renewal in past&lt;/li&gt; &lt;/ul&gt; &lt;input id="id_renewal_date" name="renewal_date" type="text" value="2023-11-08" required /&gt; &lt;br /&gt; &lt;span class="helptext"&gt; Enter date between now and 4 weeks (default 3 weeks). &lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; </code></pre></div> <h4 id="other_ways_of_using_form_template_variable">Other ways of using form template variable</h4> <p>Using <code>{{ form.as_table }}</code> as shown above, each field is rendered as a table row. You can also render each field as a list item (using <code>{{ form.as_ul }}</code>) or as a paragraph (using <code>{{ form.as_p }}</code>).</p> <p>It is also possible to have complete control over the rendering of each part of the form, by indexing its properties using dot notation. So, for example, we can access a number of separate items for our <code>renewal_date</code> field:</p> <ul> <li><code>{{ form.renewal_date }}:</code> The whole field.</li> <li><code>{{ form.renewal_date.errors }}</code>: The list of errors.</li> <li><code>{{ form.renewal_date.id_for_label }}</code>: The id of the label.</li> <li><code>{{ form.renewal_date.help_text }}</code>: The field help text.</li> </ul> <p>For more examples of how to manually render forms in templates and dynamically loop over template fields, see <a href="https://docs.djangoproject.com/en/5.0/topics/forms/#rendering-fields-manually" class="external" target="_blank">Working with forms &gt; Rendering fields manually</a> (Django docs).</p></div></section><section aria-labelledby="testing_the_page"><h3 id="testing_the_page"><a href="#testing_the_page">Testing the page</a></h3><div class="section-content"><p>If you accepted the "challenge" in <a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Authentication#challenge_yourself">Django Tutorial Part 8: User authentication and permissions</a> you'll have a view showing all books on loan in the library, which is only visible to library staff. The view might look similar to this:</p> <div class="code-example"><div class="example-header"><span class="language-name">django</span></div><pre class="brush: django notranslate"><code>{% extends "base_generic.html" %} {% block content %} &lt;h1&gt;All Borrowed Books&lt;/h1&gt; {% if bookinstance_list %} &lt;ul&gt; {% for bookinst in bookinstance_list %} &lt;li class="{% if bookinst.is_overdue %}text-danger{% endif %}"&gt; &lt;a href="{% url 'book-detail' bookinst.book.pk %}"&gt;{{ bookinst.book.title }}&lt;/a&gt; ({{ bookinst.due_back }}) {% if user.is_staff %}- {{ bookinst.borrower }}{% endif %} &lt;/li&gt; {% endfor %} &lt;/ul&gt; {% else %} &lt;p&gt;There are no books borrowed.&lt;/p&gt; {% endif %} {% endblock %} </code></pre></div> <p>We can add a link to the book renew page next to each item by appending the following template code to the list item text above. Note that this template code can only run inside the <code>{% for %}</code> loop, because that is where the <code>bookinst</code> value is defined.</p> <div class="code-example"><div class="example-header"><span class="language-name">django</span></div><pre class="brush: django notranslate"><code>{% if perms.catalog.can_mark_returned %}- &lt;a href="{% url 'renew-book-librarian' bookinst.id %}"&gt;Renew&lt;/a&gt;{% endif %} </code></pre></div> <div class="notecard note"> <p><strong>Note:</strong> Remember that your test login will need to have the permission <code>catalog.can_mark_returned</code> in order to see the new "Renew" link added above, and to access the linked page (perhaps use your superuser account).</p> </div> <p>You can alternatively manually construct a test URL like this — <code>http://127.0.0.1:8000/catalog/book/&lt;bookinstance_id&gt;/renew/</code> (a valid <code>bookinstance_id</code> can be obtained by navigating to a book detail page in your library, and copying the <code>id</code> field).</p></div></section><section aria-labelledby="what_does_it_look_like"><h3 id="what_does_it_look_like"><a href="#what_does_it_look_like">What does it look like?</a></h3><div class="section-content"><p>If you are successful, the default form will look like this:</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_renew_default.png" alt="Default form which displays the book details, due date, renewal date and a submit button appears in case the link works successfully" width="680" height="292" loading="lazy"></p> <p>The form with an invalid value entered will look like this:</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_renew_invalid.png" alt="Same form as above with an error message: invalid date - renewal in the past" width="658" height="290" loading="lazy"></p> <p>The list of all books with renew links will look like this:</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_renew_allbooks.png" alt="Displays list of all renewed books along with their details. Past due is in red." width="613" height="256" loading="lazy"></p></div></section><section aria-labelledby="modelforms"><h2 id="modelforms"><a href="#modelforms">ModelForms</a></h2><div class="section-content"><p>Creating a <code>Form</code> class using the approach described above is very flexible, allowing you to create whatever sort of form page you like and associate it with any model or models.</p> <p>However, if you just need a form to map the fields of a <em>single</em> model then your model will already define most of the information that you need in your form: fields, labels, help text and so on. Rather than recreating the model definitions in your form, it is easier to use the <a href="https://docs.djangoproject.com/en/5.0/topics/forms/modelforms/" class="external" target="_blank">ModelForm</a> helper class to create the form from your model. This <code>ModelForm</code> can then be used within your views in exactly the same way as an ordinary <code>Form</code>.</p> <p>A basic <code>ModelForm</code> containing the same field as our original <code>RenewBookForm</code> is shown below. All you need to do to create the form is add <code>class Meta</code> with the associated <code>model</code> (<code>BookInstance</code>) and a list of the model <code>fields</code> to include in the form.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>from django.forms import ModelForm from catalog.models import BookInstance class RenewBookModelForm(ModelForm): class Meta: model = BookInstance fields = ['due_back'] </code></pre></div> <div class="notecard note"> <p><strong>Note:</strong> You can also include all fields in the form using <code>fields = '__all__'</code>, or you can use <code>exclude</code> (instead of <code>fields</code>) to specify the fields <em>not</em> to include from the model).</p> <p>Neither approach is recommended because new fields added to the model are then automatically included in the form (without the developer necessarily considering possible security implications).</p> </div> <div class="notecard note"> <p><strong>Note:</strong> This might not look all that much simpler than just using a <code>Form</code> (and it isn't in this case, because we just have one field). However, if you have a lot of fields, it can considerably reduce the amount of code required!</p> </div> <p>The rest of the information comes from the model field definitions (e.g. labels, widgets, help text, error messages). If these aren't quite right, then we can override them in our <code>class Meta</code>, specifying a dictionary containing the field to change and its new value. For example, in this form, we might want a label for our field of "<em>Renewal date</em>" (rather than the default based on the field name: <em>Due Back</em>), and we also want our help text to be specific to this use case. The <code>Meta</code> below shows you how to override these fields, and you can similarly set <code>widgets</code> and <code>error_messages</code> if the defaults aren't sufficient.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>class Meta: model = BookInstance fields = ['due_back'] labels = {'due_back': _('New renewal date')} help_texts = {'due_back': _('Enter a date between now and 4 weeks (default 3).')} </code></pre></div> <p>To add validation you can use the same approach as for a normal <code>Form</code> — you define a function named <code>clean_&lt;field_name&gt;()</code> and raise <code>ValidationError</code> exceptions for invalid values. The only difference with respect to our original form is that the model field is named <code>due_back</code> and not <code>renewal_date</code>. This change is necessary since the corresponding field in <code>BookInstance</code> is called <code>due_back</code>.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>from django.forms import ModelForm from catalog.models import BookInstance class RenewBookModelForm(ModelForm): def clean_due_back(self): data = self.cleaned_data['due_back'] # Check if a date is not in the past. if data &lt; datetime.date.today(): raise ValidationError(_('Invalid date - renewal in past')) # Check if a date is in the allowed range (+4 weeks from today). if data &gt; datetime.date.today() + datetime.timedelta(weeks=4): raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead')) # Remember to always return the cleaned data. return data class Meta: model = BookInstance fields = ['due_back'] labels = {'due_back': _('Renewal date')} help_texts = {'due_back': _('Enter a date between now and 4 weeks (default 3).')} </code></pre></div> <p>The class <code>RenewBookModelForm</code> above is now functionally equivalent to our original <code>RenewBookForm</code>. You could import and use it wherever you currently use <code>RenewBookForm</code> as long as you also update the corresponding form variable name from <code>renewal_date</code> to <code>due_back</code> as in the second form declaration: <code>RenewBookModelForm(initial={'due_back': proposed_renewal_date}</code>.</p></div></section><section aria-labelledby="generic_editing_views"><h2 id="generic_editing_views"><a href="#generic_editing_views">Generic editing views</a></h2><div class="section-content"><p>The form handling algorithm we used in our function view example above represents an extremely common pattern in form editing views. Django abstracts much of this "boilerplate" for you, by creating <a href="https://docs.djangoproject.com/en/5.0/ref/class-based-views/generic-editing/" class="external" target="_blank">generic editing views</a> for creating, editing, and deleting views based on models. Not only do these handle the "view" behavior, but they automatically create the form class (a <code>ModelForm</code>) for you from the model.</p> <div class="notecard note"> <p><strong>Note:</strong> In addition to the editing views described here, there is also a <a href="https://docs.djangoproject.com/en/5.0/ref/class-based-views/generic-editing/#formview" class="external" target="_blank">FormView</a> class, which lies somewhere between our function view and the other generic views in terms of "flexibility" vs. "coding effort". Using <code>FormView</code>, you still need to create your <code>Form</code>, but you don't have to implement all of the standard form-handling patterns. Instead, you just have to provide an implementation of the function that will be called once the submission is known to be valid.</p> </div> <p>In this section, we're going to use generic editing views to create pages to add functionality to create, edit, and delete <code>Author</code> records from our library — effectively providing a basic reimplementation of parts of the Admin site (this could be useful if you need to offer admin functionality in a more flexible way than can be provided by the admin site).</p></div></section><section aria-labelledby="views"><h3 id="views"><a href="#views">Views</a></h3><div class="section-content"><p>Open the views file (<strong>django-locallibrary-tutorial/catalog/views.py</strong>) and append the following code block to the bottom of it:</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.urls import reverse_lazy from .models import Author class AuthorCreate(PermissionRequiredMixin, CreateView): model = Author fields = ['first_name', 'last_name', 'date_of_birth', 'date_of_death'] initial = {'date_of_death': '11/11/2023'} permission_required = 'catalog.add_author' class AuthorUpdate(PermissionRequiredMixin, UpdateView): model = Author # Not recommended (potential security issue if more fields added) fields = '__all__' permission_required = 'catalog.change_author' class AuthorDelete(PermissionRequiredMixin, DeleteView): model = Author success_url = reverse_lazy('authors') permission_required = 'catalog.delete_author' def form_valid(self, form): try: self.object.delete() return HttpResponseRedirect(self.success_url) except Exception as e: return HttpResponseRedirect( reverse("author-delete", kwargs={"pk": self.object.pk}) ) </code></pre></div> <p>As you can see, to create, update, or delete the views you need to derive from <code>CreateView</code>, <code>UpdateView</code>, and <code>DeleteView</code> (respectively) and then define the associated model. We also restrict calling these views to only logged in users with the <code>add_author</code>, <code>change_author</code>, and <code>delete_author</code> permissions, respectively.</p> <p>For the "create" and "update" cases you also need to specify the fields to display in the form (using the same syntax as for <code>ModelForm</code>). In this case, we show how to list them individually and the syntax to list "all" fields. You can also specify initial values for each of the fields using a dictionary of <em>field_name</em>/<em>value</em> pairs (here we arbitrarily set the date of death for demonstration purposes — you might want to remove that). By default, these views will redirect on success to a page displaying the newly created/edited model item, which in our case will be the author detail view we created in a previous tutorial. You can specify an alternative redirect location by explicitly declaring parameter <code>success_url</code>.</p> <p>The <code>AuthorDelete</code> class doesn't need to display any of the fields, so these don't need to be specified. We also set a <code>success_url</code> (as shown above), because there is no obvious default URL for Django to navigate to after successfully deleting the <code>Author</code>. Above we use the <a href="https://docs.djangoproject.com/en/5.0/ref/urlresolvers/#reverse-lazy" class="external" target="_blank"><code>reverse_lazy()</code></a> function to redirect to our author list after an author has been deleted — <code>reverse_lazy()</code> is a lazily executed version of <code>reverse()</code>, used here because we're providing a URL to a class-based view attribute.</p> <p>If deletion of authors should always succeed that would be it. Unfortunately deleting an <code>Author</code> will cause an exception if the author has an associated book, because our <a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Models#book_model"><code>Book</code> model</a> specifies <code>on_delete=models.RESTRICT</code> for the author <code>ForeignKey</code> field. To handle this case the view overrides the <a href="https://docs.djangoproject.com/en/5.0/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin.form_valid" class="external" target="_blank"><code>form_valid()</code></a> method so that if deleting the <code>Author</code> succeeds it redirects to the <code>success_url</code>, but if not, it just redirects back to the same form. We'll update the template below to make clear that you can't delete an <code>Author</code> instance that is used in any <code>Book</code>.</p></div></section><section aria-labelledby="url_configurations"><h3 id="url_configurations"><a href="#url_configurations">URL configurations</a></h3><div class="section-content"><p>Open your URL configuration file (<strong>django-locallibrary-tutorial/catalog/urls.py</strong>) and add the following configuration to the bottom of the file:</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>urlpatterns += [ path('author/create/', views.AuthorCreate.as_view(), name='author-create'), path('author/&lt;int:pk&gt;/update/', views.AuthorUpdate.as_view(), name='author-update'), path('author/&lt;int:pk&gt;/delete/', views.AuthorDelete.as_view(), name='author-delete'), ] </code></pre></div> <p>There is nothing particularly new here! You can see that the views are classes, and must hence be called via <code>.as_view()</code>, and you should be able to recognize the URL patterns in each case. We must use <code>pk</code> as the name for our captured primary key value, as this is the parameter name expected by the view classes.</p></div></section><section aria-labelledby="templates"><h3 id="templates"><a href="#templates">Templates</a></h3><div class="section-content"><p>The "create" and "update" views use the same template by default, which will be named after your model: <code>model_name_form.html</code> (you can change the suffix to something other than <strong>_form</strong> using the <code>template_name_suffix</code> field in your view, for example, <code>template_name_suffix = '_other_suffix'</code>)</p> <p>Create the template file <code>django-locallibrary-tutorial/catalog/templates/catalog/author_form.html</code> and copy the text below.</p> <div class="code-example"><div class="example-header"><span class="language-name">django</span></div><pre class="brush: django notranslate"><code>{% extends "base_generic.html" %} {% block content %} &lt;form action="" method="post"&gt; {% csrf_token %} &lt;table&gt; {{ form.as_table }} &lt;/table&gt; &lt;input type="submit" value="Submit" /&gt; &lt;/form&gt; {% endblock %} </code></pre></div> <p>This is similar to our previous forms and renders the fields using a table. Note also how again we declare the <code>{% csrf_token %}</code> to ensure that our forms are resistant to CSRF attacks.</p> <p>The "delete" view expects to find a template named with the format <code>[model_name]_confirm_delete.html</code> (again, you can change the suffix using <code>template_name_suffix</code> in your view). Create the template file <code>django-locallibrary-tutorial/catalog/templates/catalog/author_confirm_delete.html</code> and copy the text below.</p> <div class="code-example"><div class="example-header"><span class="language-name">django</span></div><pre class="brush: django notranslate"><code>{% extends "base_generic.html" %} {% block content %} &lt;h1&gt;Delete Author: {{ author }}&lt;/h1&gt; {% if author.book_set.all %} &lt;p&gt;You can't delete this author until all their books have been deleted:&lt;/p&gt; &lt;ul&gt; {% for book in author.book_set.all %} &lt;li&gt;&lt;a href="{% url 'book-detail' book.pk %}"&gt;{{book}}&lt;/a&gt; ({{book.bookinstance_set.all.count}})&lt;/li&gt; {% endfor %} &lt;/ul&gt; {% else %} &lt;p&gt;Are you sure you want to delete the author?&lt;/p&gt; &lt;form action="" method="POST"&gt; {% csrf_token %} &lt;input type="submit" action="" value="Yes, delete."&gt; &lt;/form&gt; {% endif %} {% endblock %} </code></pre></div> <p>The template should be familiar. It first checks if the author is used in any books, and if so displays the list of books that must be deleted before the author record can be deleted. If not, it displays a form asking the user to confirm they want to delete the author record.</p> <p>The final step is to hook the pages into the sidebar. First we'll add a link for creating the author into the <em>base template</em>, so that it is visible in all pages for logged in users who are considered "staff" and who have permission to create authors (<code>catalog.add_author</code>). Open <strong>/django-locallibrary-tutorial/catalog/templates/base_generic.html</strong> and add the lines that allow users with the permission to create the author (in the same block as the link that shows "All Borrowed" books). Remember to reference the URL using its name <code>'author-create'</code> as shown below.</p> <div class="code-example"><div class="example-header"><span class="language-name">django</span></div><pre class="brush: django notranslate"><code>{% if user.is_staff %} &lt;hr&gt; &lt;ul class="sidebar-nav"&gt; &lt;li&gt;Staff&lt;/li&gt; &lt;li&gt;&lt;a href="{% url 'all-borrowed' %}"&gt;All borrowed&lt;/a&gt;&lt;/li&gt; {% if perms.catalog.add_author %} &lt;li&gt;&lt;a href="{% url 'author-create' %}"&gt;Create author&lt;/a&gt;&lt;/li&gt; {% endif %} &lt;/ul&gt; {% endif %} </code></pre></div> <p>We'll add the links to update and delete authors to the author detail page. Open <strong>catalog/templates/catalog/author_detail.html</strong> and append the following code:</p> <div class="code-example"><div class="example-header"><span class="language-name">django</span></div><pre class="brush: django notranslate"><code>{% block sidebar %} {{ block.super }} {% if perms.catalog.change_author or perms.catalog.delete_author %} &lt;hr&gt; &lt;ul class="sidebar-nav"&gt; {% if perms.catalog.change_author %} &lt;li&gt;&lt;a href="{% url 'author-update' author.id %}"&gt;Update author&lt;/a&gt;&lt;/li&gt; {% endif %} {% if not author.book_set.all and perms.catalog.delete_author %} &lt;li&gt;&lt;a href="{% url 'author-delete' author.id %}"&gt;Delete author&lt;/a&gt;&lt;/li&gt; {% endif %} &lt;/ul&gt; {% endif %} {% endblock %} </code></pre></div> <p>This block overrides the <code>sidebar</code> block in the base template and then pulls in the original content using <code>{{ block.super }}</code>. It then appends links to update or delete the author, but when the user has the correct permissions and the author record isn't associated with any books.</p> <p>The pages are now ready to test!</p></div></section><section aria-labelledby="testing_the_page_2"><h3 id="testing_the_page_2"><a href="#testing_the_page_2">Testing the page</a></h3><div class="section-content"><p>First, log in to the site with an account that has author add, change and delete permissions.</p> <p>Navigate to any page, and select "Create author" in the sidebar (with URL <code>http://127.0.0.1:8000/catalog/author/create/</code>). The page should look like the screenshot below.</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_create_author.png" alt="Form Example: Create Author" width="676" height="351" loading="lazy"></p> <p>Enter values for the fields and then press <strong>Submit</strong> to save the author record. You should now be taken to a detail view for your new author, with a URL of something like <code>http://127.0.0.1:8000/catalog/author/10</code>.</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_detail_author_update.png" alt="Form Example: Author Detail showing Update and Delete links" width="680" height="427" loading="lazy"></p> <p>You can test editing the record by selecting the "Update author" link (with URL something like <code>http://127.0.0.1:8000/catalog/author/10/update/</code>) — we don't show a screenshot because it looks just like the "create" page!</p> <p>Finally, we can delete the page by selecting "Delete author" from the sidebar on the detail page. Django should display the delete page shown below if the author record isn't used in any books. Press "<strong>Yes, delete.</strong>" to remove the record and be taken to the list of all authors.</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_delete_author.png" alt="Form with option to delete author" width="561" height="194" loading="lazy"></p></div></section><section aria-labelledby="challenge_yourself"><h2 id="challenge_yourself"><a href="#challenge_yourself">Challenge yourself</a></h2><div class="section-content"><p>Create some forms to create, edit, and delete <code>Book</code> records. You can use exactly the same structure as for <code>Authors</code> (for the deletion, remember that you can't delete a <code>Book</code> until all its associated <code>BookInstance</code> records are deleted) and you must use the correct permissions. If your <strong>book_form.html</strong> template is just a copy-renamed version of the <strong>author_form.html</strong> template, then the new "create book" page will look like the screenshot below:</p> <p><img src="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_create_book.png" alt="Screenshot displaying various fields in the form like title, author, summary, ISBN, genre and language" width="595" height="521" loading="lazy"></p></div></section><section aria-labelledby="summary"><h2 id="summary"><a href="#summary">Summary</a></h2><div class="section-content"><p>Creating and handling forms can be a complicated process! Django makes it much easier by providing programmatic mechanisms to declare, render, and validate forms. Furthermore, Django provides generic form editing views that can do <em>almost all</em> the work to define pages that can create, edit, and delete records associated with a single model instance.</p> <p>There is a lot more that can be done with forms (check out our <a href="#see_also">See also</a> list below), but you should now understand how to add basic forms and form-handling code to your own websites.</p></div></section><section aria-labelledby="see_also"><h2 id="see_also"><a href="#see_also">See also</a></h2><div class="section-content"><ul> <li><a href="https://docs.djangoproject.com/en/5.0/topics/forms/" class="external" target="_blank">Working with forms</a> (Django docs)</li> <li><a href="https://docs.djangoproject.com/en/5.0/intro/tutorial04/#write-a-simple-form" class="external" target="_blank">Writing your first Django app, part 4 &gt; Writing a simple form</a> (Django docs)</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/api/" class="external" target="_blank">The Forms API</a> (Django docs)</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/fields/" class="external" target="_blank">Form fields</a> (Django docs)</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/forms/validation/" class="external" target="_blank">Form and field validation</a> (Django docs)</li> <li><a href="https://docs.djangoproject.com/en/5.0/topics/class-based-views/generic-editing/" class="external" target="_blank">Form handling with class-based views</a> (Django docs)</li> <li><a href="https://docs.djangoproject.com/en/5.0/topics/forms/modelforms/" class="external" target="_blank">Creating forms from models</a> (Django docs)</li> <li><a href="https://docs.djangoproject.com/en/5.0/ref/class-based-views/generic-editing/" class="external" target="_blank">Generic editing views</a> (Django docs)</li> </ul> <p>page(Doc) not found /en-US/docs/Learn_web_development/Extensions/Server-side/Django/authentication_and_sessions</p></div></section></article><aside class="article-footer"><div class="article-footer-inner"><div class="svg-container"><svg xmlns="http://www.w3.org/2000/svg" width="162" height="162" viewBox="0 0 162 162" fill="none" role="none"><mask id="b" fill="#fff"><path d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z"></path></mask><path stroke="url(#a)" stroke-dasharray="6, 6" stroke-width="2" d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z" mask="url(#b)" style="stroke:url(#a)" transform="translate(-63.992 -25.587)"></path><ellipse cx="8.066" cy="111.597" fill="var(--background-tertiary)" rx="53.677" ry="53.699" transform="matrix(.71707 -.697 .7243 .6895 0 0)"></ellipse><g clip-path="url(#c)" transform="translate(-63.992 -25.587)"><path fill="#9abff5" d="m144.256 137.379 32.906 12.434a4.41 4.41 0 0 1 2.559 5.667l-9.326 24.679a4.41 4.41 0 0 1-5.667 2.559l-8.226-3.108-2.332 6.17c-.466 1.233-.375 1.883-1.609 1.417l-2.253-.527c-.411-.155-.95-.594-1.206-1.161l-4.734-10.484-12.545-4.741a4.41 4.41 0 0 1-2.559-5.667l9.325-24.679a4.41 4.41 0 0 1 5.667-2.559m9.961 29.617 8.227 3.108 3.264-8.638-.498-6.768-4.113-1.555.548 7.258-4.319-1.632zm-12.339-4.663 8.226 3.108 3.264-8.637-.498-6.769-4.113-1.554.548 7.257-4.319-1.632z"></path></g><g clip-path="url(#d)" transform="translate(-63.992 -25.587)"><path fill="#81b0f3" d="M135.35 60.136 86.67 41.654c-3.346-1.27-7.124.428-8.394 3.775L64.414 81.938c-1.27 3.347.428 7.125 3.774 8.395l12.17 4.62-3.465 9.128c-.693 1.826-1.432 2.457.394 3.15l3.014 1.625c.609.231 1.637.274 2.477-.104l15.53-6.983 18.56 7.047c3.346 1.27 7.124-.428 8.395-3.775l13.862-36.51c1.27-3.346-.428-7.124-3.775-8.395M95.261 83.207l-12.17-4.62 4.852-12.779 7.19-7.017 6.085 2.31-7.725 7.51 6.389 2.426zm18.255 6.93-12.17-4.62 4.852-12.778 7.189-7.017 6.085 2.31-7.725 7.51 6.39 2.426z"></path></g><defs><clipPath id="c"><path fill="#fff" d="m198.638 146.586-65.056-24.583-24.583 65.057 65.056 24.582z"></path></clipPath><clipPath id="d"><path fill="#fff" d="m66.438 14.055 96.242 36.54-36.54 96.243-96.243-36.54z"></path></clipPath><linearGradient id="a" x1="97.203" x2="199.995" y1="47.04" y2="152.793" gradientUnits="userSpaceOnUse"><stop stop-color="#086DFC"></stop><stop offset="0.246" stop-color="#2C81FA"></stop><stop offset="0.516" stop-color="#5497F8"></stop><stop offset="0.821" stop-color="#80B0F6"></stop><stop offset="1" stop-color="#9ABFF5"></stop></linearGradient></defs></svg></div><h2>Help improve MDN</h2><fieldset class="feedback"><label>Was this page helpful to you?</label><div class="button-container"><button type="button" class="button primary has-icon yes"><span class="button-wrap"><span class="icon icon-thumbs-up "></span>Yes</span></button><button type="button" class="button primary has-icon no"><span class="button-wrap"><span class="icon icon-thumbs-down "></span>No</span></button></div></fieldset><a class="contribute" href="https://github.com/mdn/content/blob/main/CONTRIBUTING.md" title="This will take you to our contribution guidelines on GitHub." target="_blank" rel="noopener noreferrer">Learn how to contribute</a>.<p class="last-modified-date">This page was last modified on<!-- --> <time dateTime="2024-12-19T15:37:45.000Z">Dec 19, 2024</time> by<!-- --> <a href="/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/contributors.txt" rel="nofollow">MDN contributors</a>.</p><div id="on-github" class="on-github"><a href="https://github.com/mdn/content/blob/main/files/en-us/learn_web_development/extensions/server-side/django/forms/index.md?plain=1" title="Folder: en-us/learn_web_development/extensions/server-side/django/forms (Opens in a new tab)" target="_blank" rel="noopener noreferrer">View this page on GitHub</a> <!-- -->•<!-- --> <a href="https://github.com/mdn/content/issues/new?template=page-report.yml&amp;mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FLearn_web_development%2FExtensions%2FServer-side%2FDjango%2FForms&amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EPage+report+details%3C%2Fsummary%3E%0A%0A*+Folder%3A+%60en-us%2Flearn_web_development%2Fextensions%2Fserver-side%2Fdjango%2Fforms%60%0A*+MDN+URL%3A+https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FLearn_web_development%2FExtensions%2FServer-side%2FDjango%2FForms%0A*+GitHub+URL%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Fcontent%2Fblob%2Fmain%2Ffiles%2Fen-us%2Flearn_web_development%2Fextensions%2Fserver-side%2Fdjango%2Fforms%2Findex.md%0A*+Last+commit%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Fcontent%2Fcommit%2F5b20f5f4265f988f80f513db0e4b35c7e0cd70dc%0A*+Document+last+modified%3A+2024-12-19T15%3A37%3A45.000Z%0A%0A%3C%2Fdetails%3E" title="This will take you to GitHub to file a new issue." target="_blank" rel="noopener noreferrer">Report a problem with this content</a></div></div></aside></main></div></div><footer id="nav-footer" class="page-footer"><div class="page-footer-grid"><div class="page-footer-logo-col"><a href="/" class="mdn-footer-logo" aria-label="MDN homepage"><svg width="48" height="17" viewBox="0 0 48 17" fill="none" xmlns="http://www.w3.org/2000/svg"><title id="mdn-footer-logo-svg">MDN logo</title><path d="M20.04 16.512H15.504V10.416C15.504 9.488 15.344 8.824 15.024 8.424C14.72 8.024 14.264 7.824 13.656 7.824C12.92 7.824 12.384 8.064 12.048 8.544C11.728 9.024 11.568 9.64 11.568 10.392V14.184H13.008V16.512H8.472V10.416C8.472 9.488 8.312 8.824 7.992 8.424C7.688 8.024 7.232 7.824 6.624 7.824C5.872 7.824 5.336 8.064 5.016 8.544C4.696 9.024 4.536 9.64 4.536 10.392V14.184H6.6V16.512H0V14.184H1.44V8.04H0.024V5.688H4.536V7.32C5.224 6.088 6.32 5.472 7.824 5.472C8.608 5.472 9.328 5.664 9.984 6.048C10.64 6.432 11.096 7.016 11.352 7.8C11.992 6.248 13.168 5.472 14.88 5.472C15.856 5.472 16.72 5.776 17.472 6.384C18.224 6.992 18.6 7.936 18.6 9.216V14.184H20.04V16.512Z" fill="currentColor"></path><path d="M33.6714 16.512H29.1354V14.496C28.8314 15.12 28.3834 15.656 27.7914 16.104C27.1994 16.536 26.4154 16.752 25.4394 16.752C24.0154 16.752 22.8954 16.264 22.0794 15.288C21.2634 14.312 20.8554 12.984 20.8554 11.304C20.8554 9.688 21.2554 8.312 22.0554 7.176C22.8554 6.04 24.0634 5.472 25.6794 5.472C26.5594 5.472 27.2794 5.648 27.8394 6C28.3994 6.352 28.8314 6.8 29.1354 7.344V2.352H26.9754V0H32.2314V14.184H33.6714V16.512ZM29.1354 11.04V10.776C29.1354 9.88 28.8954 9.184 28.4154 8.688C27.9514 8.176 27.3674 7.92 26.6634 7.92C25.9754 7.92 25.3674 8.176 24.8394 8.688C24.3274 9.2 24.0714 10.008 24.0714 11.112C24.0714 12.152 24.3114 12.944 24.7914 13.488C25.2714 14.032 25.8394 14.304 26.4954 14.304C27.3114 14.304 27.9514 13.96 28.4154 13.272C28.8954 12.584 29.1354 11.84 29.1354 11.04Z" fill="currentColor"></path><path d="M47.9589 16.512H41.9829V14.184H43.4229V10.416C43.4229 9.488 43.2629 8.824 42.9429 8.424C42.6389 8.024 42.1829 7.824 41.5749 7.824C40.8389 7.824 40.2709 8.056 39.8709 8.52C39.4709 8.968 39.2629 9.56 39.2469 10.296V14.184H40.6869V16.512H34.7109V14.184H36.1509V8.04H34.5909V5.688H39.2469V7.344C39.9669 6.096 41.1269 5.472 42.7269 5.472C43.7509 5.472 44.6389 5.776 45.3909 6.384C46.1429 6.992 46.5189 7.936 46.5189 9.216V14.184H47.9589V16.512Z" fill="currentColor"></path></svg></a><p>Your blueprint for a better internet.</p><ul class="social-icons"><li><a href="https://mastodon.social/@mdn" target="_blank" rel="me noopener noreferrer"><span class="icon icon-mastodon"></span><span class="visually-hidden">MDN on Mastodon</span></a></li><li><a href="https://twitter.com/mozdevnet" target="_blank" rel="noopener noreferrer"><span class="icon icon-twitter-x"></span><span class="visually-hidden">MDN on X (formerly Twitter)</span></a></li><li><a href="https://github.com/mdn/" target="_blank" rel="noopener noreferrer"><span class="icon icon-github-mark-small"></span><span class="visually-hidden">MDN on GitHub</span></a></li><li><a href="/en-US/blog/rss.xml" target="_blank"><span class="icon icon-feed"></span><span class="visually-hidden">MDN Blog RSS Feed</span></a></li></ul></div><div class="page-footer-nav-col-1"><h2 class="footer-nav-heading">MDN</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a href="/en-US/about">About</a></li><li class="footer-nav-item"><a href="/en-US/blog/">Blog</a></li><li class="footer-nav-item"><a href="https://www.mozilla.org/en-US/careers/listings/?team=ProdOps" target="_blank" rel="noopener noreferrer">Careers</a></li><li class="footer-nav-item"><a href="/en-US/advertising">Advertise with us</a></li></ul></div><div class="page-footer-nav-col-2"><h2 class="footer-nav-heading">Support</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="https://support.mozilla.org/products/mdn-plus">Product help</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/docs/MDN/Community/Issues">Report an issue</a></li></ul></div><div class="page-footer-nav-col-3"><h2 class="footer-nav-heading">Our communities</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/community">MDN Community</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="https://discourse.mozilla.org/c/mdn/236" target="_blank" rel="noopener noreferrer">MDN Forum</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/discord" target="_blank" rel="noopener noreferrer">MDN Chat</a></li></ul></div><div class="page-footer-nav-col-4"><h2 class="footer-nav-heading">Developers</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/docs/Web">Web Technologies</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/docs/Learn">Learn Web Development</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/plus">MDN Plus</a></li><li class="footer-nav-item"><a href="https://hacks.mozilla.org/" target="_blank" rel="noopener noreferrer">Hacks Blog</a></li></ul></div><div class="page-footer-moz"><a href="https://www.mozilla.org/" class="footer-moz-logo-link" target="_blank" rel="noopener noreferrer"><svg xmlns="http://www.w3.org/2000/svg" width="137" height="32" fill="none" viewBox="0 0 267.431 62.607"><path fill="currentColor" d="m13.913 23.056 5.33 25.356h2.195l5.33-25.356h14.267v38.976h-7.578V29.694h-2.194l-7.264 32.337h-7.343L9.418 29.694H7.223v32.337H-.354V23.056Zm47.137 9.123c9.12 0 14.423 5.385 14.423 15.214s-5.33 15.214-14.423 15.214c-9.12 0-14.423-5.385-14.423-15.214 0-9.855 5.304-15.214 14.423-15.214m0 24.363c4.285 0 6.428-2.196 6.428-7.032v-4.287c0-4.836-2.143-7.032-6.428-7.032s-6.428 2.196-6.428 7.032v4.287c0 4.836 2.143 7.032 6.428 7.032m18.473-.157 15.47-18.01h-15.26v-5.647h24.352v5.646L88.616 56.385h15.704v5.646H79.523Zm29.318-23.657h11.183V62.03h-7.578V38.375h-3.632v-5.646zm3.605-9.672h7.578v5.646h-7.578zm13.17 0h11.21v38.976h-7.578v-33.33h-3.632zm16.801 0H153.6v38.976h-7.577v-33.33h-3.632v-5.646zm29.03 9.123c4.442 0 7.394 2.143 8.231 5.881h2.194v-5.332h9.276v5.646h-3.632v18.011h3.632v5.646h-4.442c-3.135 0-4.834-1.699-4.834-4.836V56.7h-2.194c-.81 3.738-3.789 5.881-8.23 5.881-6.978 0-11.916-5.829-11.916-15.214 0-9.384 4.938-15.187 11.915-15.187m2.3 24.363c4.284 0 6.192-2.196 6.192-7.032v-4.287c0-4.836-1.908-7.032-6.193-7.032-4.18 0-6.193 2.196-6.193 7.032v4.287c0 4.836 2.012 7.032 6.193 7.032m48.34 5.489h-7.577V0h7.577zm6.585-29.643h32.165v-2.196l-21.295-7.634v-6.143l21.295-7.633V6.588h-25.345V0h32.165v12.522l-17.35 5.881V20.6l17.35 5.882v12.521h-38.985zm0-25.801h6.794v6.796h-6.794z"></path></svg></a><ul class="footer-moz-list"><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Website Privacy Notice</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/#cookies" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Cookies</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/legal/terms/mozilla" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Legal</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/governance/policies/participation/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Community Participation Guidelines</a></li></ul></div><div class="page-footer-legal"><p id="license" class="page-footer-legal-text">Visit<!-- --> <a href="https://www.mozilla.org" target="_blank" rel="noopener noreferrer">Mozilla Corporation’s</a> <!-- -->not-for-profit parent, the<!-- --> <a target="_blank" rel="noopener noreferrer" href="https://foundation.mozilla.org/">Mozilla Foundation</a>.<br/>Portions of this content are ©1998–<!-- -->2025<!-- --> by individual mozilla.org contributors. Content available under<!-- --> <a href="/en-US/docs/MDN/Writing_guidelines/Attrib_copyright_license">a Creative Commons license</a>.</p></div></div></footer></div><script type="application/json" id="hydration">{"url":"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms","doc":{"body":[{"type":"prose","value":{"id":null,"title":null,"isH3":false,"content":"<ul class=\"prev-next\"><li><a class=\"button secondary\" href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Sessions\"><span class=\"button-wrap\"> Previous </span></a></li><li><a class=\"button secondary\" href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django\"><span class=\"button-wrap\"> Overview: Django Web Framework (Python)</span></a></li><li><a class=\"button secondary\" href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Testing\"><span class=\"button-wrap\"> Next </span></a></li></ul>\n<p>In this tutorial, we'll show you how to work with HTML Forms in Django, and, in particular, the easiest way to write forms to create, update, and delete model instances. As part of this demonstration, we'll extend the <a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Tutorial_local_library_website\">LocalLibrary</a> website so that librarians can renew books, and create, update, and delete authors using our own forms (rather than using the admin application).</p>\n<figure class=\"table-container\"><table>\n <tbody>\n <tr>\n <th scope=\"row\">Prerequisites:</th>\n <td>\n Complete all previous tutorial topics, including\n <a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Authentication\">Django Tutorial Part 8: User authentication and permissions</a>.\n </td>\n </tr>\n <tr>\n <th scope=\"row\">Objective:</th>\n <td>\n To understand how to write forms to get information from users and update the database.\n To understand how the generic class-based editing views can vastly simplify creating forms for working with a single model.\n </td>\n </tr>\n </tbody>\n</table></figure>"}},{"type":"prose","value":{"id":"overview","title":"Overview","isH3":false,"content":"<p>An <a href=\"/en-US/docs/Learn_web_development/Extensions/Forms\">HTML Form</a> is a group of one or more fields/widgets on a web page, which can be used to collect information from users for submission to a server. Forms are a flexible mechanism for collecting user input because there are suitable widgets for entering many different types of data, including text boxes, checkboxes, radio buttons, date pickers and so on. Forms are also a relatively secure way of sharing data with the server, as they allow us to send data in <code>POST</code> requests with cross-site request forgery protection.</p>\n<p>While we haven't created any forms in this tutorial so far, we've already encountered them in the Django Admin site — for example, the screenshot below shows a form for editing one of our <a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Models\">Book</a> models, comprised of a number of selection lists and text editors.</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/admin_book_add.png\" alt=\"Admin Site - Book Add\" width=\"841\" height=\"780\" loading=\"lazy\"></p>\n<p>Working with forms can be complicated! Developers need to write HTML for the form, validate and properly sanitize entered data on the server (and possibly also in the browser), repost the form with error messages to inform users of any invalid fields, handle the data when it has successfully been submitted, and finally respond to the user in some way to indicate success. <em>Django Forms</em> take a lot of the work out of all these steps, by providing a framework that lets you define forms and their fields programmatically, and then use these objects to both generate the form HTML code and handle much of the validation and user interaction.</p>\n<p>In this tutorial, we're going to show you a few of the ways you can create and work with forms, and in particular, how the generic editing views can significantly reduce the amount of work you need to do to create forms to manipulate your models. Along the way, we'll extend our <em>LocalLibrary</em> application by adding a form to allow librarians to renew library books, and we'll create pages to create, edit and delete books and authors (reproducing a basic version of the form shown above for editing books).</p>"}},{"type":"prose","value":{"id":"html_forms","title":"HTML Forms","isH3":false,"content":"<p>First, a brief overview of <a href=\"/en-US/docs/Learn_web_development/Extensions/Forms\">HTML Forms</a>. Consider a simple HTML form, with a single text field for entering the name of some \"team\", and its associated label:</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/form_example_name_field.png\" alt=\"Simple name field example in HTML form\" width=\"399\" height=\"44\" loading=\"lazy\"></p>\n<p>The form is defined in HTML as a collection of elements inside <code>&lt;form&gt;…&lt;/form&gt;</code> tags, containing at least one <code>input</code> element of <code>type=\"submit\"</code>.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code>&lt;form action=\"/team_name_url/\" method=\"post\"&gt;\n &lt;label for=\"team_name\"&gt;Enter name: &lt;/label&gt;\n &lt;input\n id=\"team_name\"\n type=\"text\"\n name=\"name_field\"\n value=\"Default name for team.\" /&gt;\n &lt;input type=\"submit\" value=\"OK\" /&gt;\n&lt;/form&gt;\n</code></pre></div>\n<p>While here we just have one text field for entering the team name, a form <em>may</em> have any number of other input elements and their associated labels. The field's <code>type</code> attribute defines what sort of widget will be displayed. The <code>name</code> and <code>id</code> of the field are used to identify the field in JavaScript/CSS/HTML, while <code>value</code> defines the initial value for the field when it is first displayed. The matching team label is specified using the <code>label</code> tag (see \"Enter name\" above), with a <code>for</code> field containing the <code>id</code> value of the associated <code>input</code>.</p>\n<p>The <code>submit</code> input will be displayed as a button by default.\nThis can be pressed to upload the data in all the other input elements in the form to the server (in this case, just the <code>team_name</code> field).\nThe form attributes define the HTTP <code>method</code> used to send the data and the destination of the data on the server (<code>action</code>):</p>\n<ul>\n<li>\n<p><code>action</code>: The resource/URL where data is to be sent for processing when the form is submitted. If this is not set (or set to an empty string), then the form will be submitted back to the current page URL.</p>\n</li>\n<li>\n<p><code>method</code>: The HTTP method used to send the data: <em>post</em> or <em>get</em>.</p>\n<ul>\n<li>The <code>POST</code> method should always be used if the data is going to result in a change to the server's database, because it can be made more resistant to cross-site forgery request attacks.</li>\n<li>The <code>GET</code> method should only be used for forms that don't change user data (for example, a search form). It is recommended for when you want to be able to bookmark or share the URL.</li>\n</ul>\n</li>\n</ul>\n<p>The role of the server is first to render the initial form state — either containing blank fields or pre-populated with initial values. After the user presses the submit button, the server will receive the form data with values from the web browser and must validate the information. If the form contains invalid data, the server should display the form again, this time with user-entered data in \"valid\" fields and messages to describe the problem for the invalid fields. Once the server gets a request with all valid form data, it can perform an appropriate action (such as: saving the data, returning the result of a search, uploading a file, etc.) and then notify the user.</p>\n<p>As you can imagine, creating the HTML, validating the returned data, re-displaying the entered data with error reports if needed, and performing the desired operation on valid data can all take quite a lot of effort to \"get right\". Django makes this a lot easier by taking away some of the heavy lifting and repetitive code!</p>"}},{"type":"prose","value":{"id":"django_form_handling_process","title":"Django form handling process","isH3":false,"content":"<p>Django's form handling uses all of the same techniques that we learned about in previous tutorials (for displaying information about our models): the view gets a request, performs any actions required including reading data from the models, then generates and returns an HTML page (from a template, into which we pass a <em>context</em> containing the data to be displayed). What makes things more complicated is that the server also needs to be able to process data provided by the user, and redisplay the page if there are any errors.</p>\n<p>A process flowchart of how Django handles form requests is shown below, starting with a request for a page containing a form (shown in green).</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/form_handling_-_standard.png\" alt=\"Updated form handling process doc.\" width=\"1180\" height=\"840\" loading=\"lazy\"></p>\n<p>Based on the diagram above, the main things that Django's form handling does are:</p>\n<ol>\n<li>\n<p>Display the default form the first time it is requested by the user.</p>\n<ul>\n<li>The form may contain blank fields if you're creating a new record, or it may be pre-populated with initial values (for example, if you are changing a record, or have useful default initial values).</li>\n<li>The form is referred to as <em>unbound</em> at this point, because it isn't associated with any user-entered data (though it may have initial values).</li>\n</ul>\n</li>\n<li>\n<p>Receive data from a submit request and bind it to the form.</p>\n<ul>\n<li>Binding data to the form means that the user-entered data and any errors are available when we need to redisplay the form.</li>\n</ul>\n</li>\n<li>\n<p>Clean and validate the data.</p>\n<ul>\n<li>Cleaning the data performs sanitization of the input fields, such as removing invalid characters that might be used to send malicious content to the server, and converts them into consistent Python types.</li>\n<li>Validation checks that the values are appropriate for the field (for example, that they are in the right date range, aren't too short or too long, etc.)</li>\n</ul>\n</li>\n<li>\n<p>If any data is invalid, re-display the form, this time with any user populated values and error messages for the problem fields.</p>\n</li>\n<li>\n<p>If all data is valid, perform required actions (such as save the data, send an email, return the result of a search, upload a file, and so on).</p>\n</li>\n<li>\n<p>Once all actions are complete, redirect the user to another page.</p>\n</li>\n</ol>\n<p>Django provides a number of tools and approaches to help you with the tasks detailed above. The most fundamental is the <code>Form</code> class, which simplifies both generation of form HTML and data cleaning/validation. In the next section, we describe how forms work using the practical example of a page to allow librarians to renew books.</p>\n<div class=\"notecard note\">\n<p><strong>Note:</strong>\nUnderstanding how <code>Form</code> is used will help you when we discuss Django's more \"high level\" form framework classes.</p>\n</div>"}},{"type":"prose","value":{"id":"renew-book_form_using_a_form_and_function_view","title":"Renew-book form using a Form and function view","isH3":false,"content":"<p>Next, we're going to add a page to allow librarians to renew borrowed books. To do this we'll create a form that allows users to enter a date value. We'll seed the field with an initial value 3 weeks from the current date (the normal borrowing period), and add some validation to ensure that the librarian can't enter a date in the past or a date too far in the future. When a valid date has been entered, we'll write it to the current record's <code>BookInstance.due_back</code> field.</p>\n<p>The example will use a function-based view and a <code>Form</code> class. The following sections explain how forms work, and the changes you need to make to our ongoing <em>LocalLibrary</em> project.</p>"}},{"type":"prose","value":{"id":"form","title":"Form","isH3":true,"content":"<p>The <code>Form</code> class is the heart of Django's form handling system. It specifies the fields in the form, their layout, display widgets, labels, initial values, valid values, and (once validated) the error messages associated with invalid fields. The class also provides methods for rendering itself in templates using predefined formats (tables, lists, etc.) or for getting the value of any element (enabling fine-grained manual rendering).</p>\n<h4 id=\"declaring_a_form\">Declaring a Form</h4>\n<p>The declaration syntax for a <code>Form</code> is very similar to that for declaring a <code>Model</code>, and shares the same field types (and some similar parameters). This makes sense because in both cases we need to ensure that each field handles the right types of data, is constrained to valid data, and has a description for display/documentation.</p>\n<p>Form data is stored in an application's forms.py file, inside the application directory. Create and open the file <strong>django-locallibrary-tutorial/catalog/forms.py</strong>. To create a <code>Form</code>, we import the <code>forms</code> library, derive from the <code>Form</code> class, and declare the form's fields. A very basic form class for our library book renewal form is shown below — add this to your new file:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>from django import forms\n\nclass RenewBookForm(forms.Form):\n renewal_date = forms.DateField(help_text=\"Enter a date between now and 4 weeks (default 3).\")\n</code></pre></div>\n<h4 id=\"form_fields\">Form fields</h4>\n<p>In this case, we have a single <a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#datefield\" class=\"external\" target=\"_blank\"><code>DateField</code></a> for entering the renewal date that will render in HTML with a blank value, the default label \"<em>Renewal date:</em>\", and some helpful usage text: \"<em>Enter a date between now and 4 weeks (default 3 weeks).</em>\" As none of the other optional arguments are specified the field will accept dates using the <a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#django.forms.DateField.input_formats\" class=\"external\" target=\"_blank\">input_formats</a>: YYYY-MM-DD (2024-11-06), MM/DD/YYYY (02/26/2024), MM/DD/YY (10/25/24), and will be rendered using the default <a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#widget\" class=\"external\" target=\"_blank\">widget</a>: <a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/widgets/#django.forms.DateInput\" class=\"external\" target=\"_blank\">DateInput</a>.</p>\n<p>There are many other types of form fields, which you will largely recognize from their similarity to the equivalent model field classes:</p>\n<ul>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#booleanfield\" class=\"external\" target=\"_blank\"><code>BooleanField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#charfield\" class=\"external\" target=\"_blank\"><code>CharField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#choicefield\" class=\"external\" target=\"_blank\"><code>ChoiceField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#typedchoicefield\" class=\"external\" target=\"_blank\"><code>TypedChoiceField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#datefield\" class=\"external\" target=\"_blank\"><code>DateField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#datetimefield\" class=\"external\" target=\"_blank\"><code>DateTimeField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#decimalfield\" class=\"external\" target=\"_blank\"><code>DecimalField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#durationfield\" class=\"external\" target=\"_blank\"><code>DurationField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#emailfield\" class=\"external\" target=\"_blank\"><code>EmailField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#filefield\" class=\"external\" target=\"_blank\"><code>FileField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#filepathfield\" class=\"external\" target=\"_blank\"><code>FilePathField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#floatfield\" class=\"external\" target=\"_blank\"><code>FloatField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#imagefield\" class=\"external\" target=\"_blank\"><code>ImageField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#integerfield\" class=\"external\" target=\"_blank\"><code>IntegerField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#genericipaddressfield\" class=\"external\" target=\"_blank\"><code>GenericIPAddressField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#multiplechoicefield\" class=\"external\" target=\"_blank\"><code>MultipleChoiceField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#typedmultiplechoicefield\" class=\"external\" target=\"_blank\"><code>TypedMultipleChoiceField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#nullbooleanfield\" class=\"external\" target=\"_blank\"><code>NullBooleanField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#regexfield\" class=\"external\" target=\"_blank\"><code>RegexField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#slugfield\" class=\"external\" target=\"_blank\"><code>SlugField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#timefield\" class=\"external\" target=\"_blank\"><code>TimeField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#urlfield\" class=\"external\" target=\"_blank\"><code>URLField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#uuidfield\" class=\"external\" target=\"_blank\"><code>UUIDField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#combofield\" class=\"external\" target=\"_blank\"><code>ComboField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#multivaluefield\" class=\"external\" target=\"_blank\"><code>MultiValueField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#splitdatetimefield\" class=\"external\" target=\"_blank\"><code>SplitDateTimeField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#modelmultiplechoicefield\" class=\"external\" target=\"_blank\"><code>ModelMultipleChoiceField</code></a></li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#modelchoicefield\" class=\"external\" target=\"_blank\"><code>ModelChoiceField</code></a></li>\n</ul>\n<p>The arguments that are common to most fields are listed below (these have sensible default values):</p>\n<ul>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#required\" class=\"external\" target=\"_blank\"><code>required</code></a>: If <code>True</code>, the field may not be left blank or given a <code>None</code> value. Fields are required by default, so you would set <code>required=False</code> to allow blank values in the form.</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#label\" class=\"external\" target=\"_blank\"><code>label</code></a>: The label to use when rendering the field in HTML. If a <a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#label\" class=\"external\" target=\"_blank\">label</a> is not specified, Django will create one from the field name by capitalizing the first letter and replacing underscores with spaces (e.g. <em>Renewal date</em>).</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#label-suffix\" class=\"external\" target=\"_blank\"><code>label_suffix</code></a>: By default, a colon is displayed after the label (e.g. Renewal date​<strong>:</strong>). This argument allows you to specify a different suffix containing other character(s).</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#initial\" class=\"external\" target=\"_blank\"><code>initial</code></a>: The initial value for the field when the form is displayed.</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#widget\" class=\"external\" target=\"_blank\"><code>widget</code></a>: The display widget to use.</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#help-text\" class=\"external\" target=\"_blank\"><code>help_text</code></a> (as seen in the example above): Additional text that can be displayed in forms to explain how to use the field.</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#error-messages\" class=\"external\" target=\"_blank\"><code>error_messages</code></a>: A list of error messages for the field. You can override these with your own messages if needed.</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#validators\" class=\"external\" target=\"_blank\"><code>validators</code></a>: A list of functions that will be called on the field when it is validated.</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#localize\" class=\"external\" target=\"_blank\"><code>localize</code></a>: Enables the localization of form data input (see link for more information).</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/#disabled\" class=\"external\" target=\"_blank\"><code>disabled</code></a>: The field is displayed but its value cannot be edited if this is <code>True</code>. The default is <code>False</code>.</li>\n</ul>\n<h4 id=\"validation\">Validation</h4>\n<p>Django provides numerous places where you can validate your data. The easiest way to validate a single field is to override the method <code>clean_&lt;field_name&gt;()</code> for the field you want to check. So for example, we can validate that entered <code>renewal_date</code> values are between now and 4 weeks by implementing <code>clean_renewal_date()</code> as shown below.</p>\n<p>Update your forms.py file so it looks like this:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>import datetime\n\nfrom django import forms\n\nfrom django.core.exceptions import ValidationError\nfrom django.utils.translation import gettext_lazy as _\n\nclass RenewBookForm(forms.Form):\n renewal_date = forms.DateField(help_text=\"Enter a date between now and 4 weeks (default 3).\")\n\n def clean_renewal_date(self):\n data = self.cleaned_data['renewal_date']\n\n # Check if a date is not in the past.\n if data &lt; datetime.date.today():\n raise ValidationError(_('Invalid date - renewal in past'))\n\n # Check if a date is in the allowed range (+4 weeks from today).\n if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):\n raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))\n\n # Remember to always return the cleaned data.\n return data\n</code></pre></div>\n<p>There are two important things to note. The first is that we get our data using <code>self.cleaned_data['renewal_date']</code> and that we return this data whether or not we change it at the end of the function.\nThis step gets us the data \"cleaned\" and sanitized of potentially unsafe input using the default validators, and converted into the correct standard type for the data (in this case a Python <code>datetime.datetime</code> object).</p>\n<p>The second point is that if a value falls outside our range we raise a <code>ValidationError</code>, specifying the error text that we want to display in the form if an invalid value is entered.\nThe example above also wraps this text in one of Django's <a href=\"https://docs.djangoproject.com/en/5.0/topics/i18n/translation/\" class=\"external\" target=\"_blank\">translation functions</a>, <code>gettext_lazy()</code> (imported as <code>_()</code>), which is good practice if you want to translate your site later.</p>\n<div class=\"notecard note\">\n<p><strong>Note:</strong>\nThere are numerous other methods and examples for validating forms in <a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/validation/\" class=\"external\" target=\"_blank\">Form and field validation</a> (Django docs). For example, in cases where you have multiple fields that depend on each other, you can override the <a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/api/#django.forms.Form.clean\" class=\"external\" target=\"_blank\">Form.clean()</a> function and again raise a <code>ValidationError</code>.</p>\n</div>\n<p>That's all we need for the form in this example!</p>"}},{"type":"prose","value":{"id":"url_configuration","title":"URL configuration","isH3":true,"content":"<p>Before we create our view, let's add a URL configuration for the <em>renew-books</em> page. Copy the following configuration to the bottom of <strong>django-locallibrary-tutorial/catalog/urls.py</strong>:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>urlpatterns += [\n path('book/&lt;uuid:pk&gt;/renew/', views.renew_book_librarian, name='renew-book-librarian'),\n]\n</code></pre></div>\n<p>The URL configuration will redirect URLs with the format <strong>/catalog/book/<em>&lt;bookinstance_id&gt;</em>/renew/</strong> to the function named <code>renew_book_librarian()</code> in <strong>views.py</strong>, and send the <code>BookInstance</code> id as the parameter named <code>pk</code>. The pattern only matches if <code>pk</code> is a correctly formatted <code>uuid</code>.</p>\n<div class=\"notecard note\">\n<p><strong>Note:</strong>\nWe can name our captured URL data anything we like, because we have complete control over the view function (we're not using a generic detail view class that expects parameters with a certain name). However, <code>pk</code> short for \"primary key\", is a reasonable convention to use!</p>\n</div>"}},{"type":"prose","value":{"id":"view","title":"View","isH3":true,"content":"<p>As discussed in the <a href=\"#django_form_handling_process\">Django form handling process</a> above, the view has to render the default form when it is first called and then either re-render it with error messages if the data is invalid, or process the data and redirect to a new page if the data is valid. In order to perform these different actions, the view has to be able to know whether it is being called for the first time to render the default form, or a subsequent time to validate data.</p>\n<p>For forms that use a <code>POST</code> request to submit information to the server, the most common pattern is for the view to test against the <code>POST</code> request type (<code>if request.method == 'POST':</code>) to identify form validation requests and <code>GET</code> (using an <code>else</code> condition) to identify the initial form creation request. If you want to submit your data using a <code>GET</code> request, then a typical approach for identifying whether this is the first or subsequent view invocation is to read the form data (e.g. to read a hidden value in the form).</p>\n<p>The book renewal process will be writing to our database, so, by convention, we use the <code>POST</code> request approach.\nThe code fragment below shows the (very standard) pattern for this sort of function view.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>import datetime\n\nfrom django.shortcuts import render, get_object_or_404\nfrom django.http import HttpResponseRedirect\nfrom django.urls import reverse\n\nfrom catalog.forms import RenewBookForm\n\ndef renew_book_librarian(request, pk):\n book_instance = get_object_or_404(BookInstance, pk=pk)\n\n # If this is a POST request then process the Form data\n if request.method == 'POST':\n\n # Create a form instance and populate it with data from the request (binding):\n form = RenewBookForm(request.POST)\n\n # Check if the form is valid:\n if form.is_valid():\n # process the data in form.cleaned_data as required (here we just write it to the model due_back field)\n book_instance.due_back = form.cleaned_data['renewal_date']\n book_instance.save()\n\n # redirect to a new URL:\n return HttpResponseRedirect(reverse('all-borrowed'))\n\n # If this is a GET (or any other method) create the default form.\n else:\n proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)\n form = RenewBookForm(initial={'renewal_date': proposed_renewal_date})\n\n context = {\n 'form': form,\n 'book_instance': book_instance,\n }\n\n return render(request, 'catalog/book_renew_librarian.html', context)\n</code></pre></div>\n<p>First, we import our form (<code>RenewBookForm</code>) and a number of other useful objects/methods used in the body of the view function:</p>\n<ul>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/topics/http/shortcuts/#get-object-or-404\" class=\"external\" target=\"_blank\"><code>get_object_or_404()</code></a>: Returns a specified object from a model based on its primary key value, and raises an <code>Http404</code> exception (not found) if the record does not exist.</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/request-response/#django.http.HttpResponseRedirect\" class=\"external\" target=\"_blank\"><code>HttpResponseRedirect</code></a>: This creates a redirect to a specified URL (HTTP status code 302).</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/urlresolvers/#django.urls.reverse\" class=\"external\" target=\"_blank\"><code>reverse()</code></a>: This generates a URL from a URL configuration name and a set of arguments. It is the Python equivalent of the <code>url</code> tag that we've been using in our templates.</li>\n<li><a href=\"https://docs.python.org/3/library/datetime.html\" class=\"external\" target=\"_blank\"><code>datetime</code></a>: A Python library for manipulating dates and times.</li>\n</ul>\n<p>In the view, we first use the <code>pk</code> argument in <code>get_object_or_404()</code> to get the current <code>BookInstance</code> (if this does not exist, the view will immediately exit and the page will display a \"not found\" error).\nIf this is <em>not</em> a <code>POST</code> request (handled by the <code>else</code> clause) then we create the default form passing in an <code>initial</code> value for the <code>renewal_date</code> field, 3 weeks from the current date.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>book_instance = get_object_or_404(BookInstance, pk=pk)\n\n# If this is a GET (or any other method) create the default form\nelse:\n proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)\n form = RenewBookForm(initial={'renewal_date': proposed_renewal_date})\n\ncontext = {\n 'form': form,\n 'book_instance': book_instance,\n}\n\nreturn render(request, 'catalog/book_renew_librarian.html', context)\n</code></pre></div>\n<p>After creating the form, we call <code>render()</code> to create the HTML page, specifying the template and a context that contains our form. In this case, the context also contains our <code>BookInstance</code>, which we'll use in the template to provide information about the book we're renewing.</p>\n<p>However, if this is a <code>POST</code> request, then we create our <code>form</code> object and populate it with data from the request. This process is called \"binding\" and allows us to validate the form.</p>\n<p>We then check if the form is valid, which runs all the validation code on all of the fields — including both the generic code to check that our date field is actually a valid date and our specific form's <code>clean_renewal_date()</code> function to check the date is in the right range.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>book_instance = get_object_or_404(BookInstance, pk=pk)\n\n# If this is a POST request then process the Form data\nif request.method == 'POST':\n\n # Create a form instance and populate it with data from the request (binding):\n form = RenewBookForm(request.POST)\n\n # Check if the form is valid:\n if form.is_valid():\n # process the data in form.cleaned_data as required (here we just write it to the model due_back field)\n book_instance.due_back = form.cleaned_data['renewal_date']\n book_instance.save()\n\n # redirect to a new URL:\n return HttpResponseRedirect(reverse('all-borrowed'))\n\ncontext = {\n 'form': form,\n 'book_instance': book_instance,\n}\n\nreturn render(request, 'catalog/book_renew_librarian.html', context)\n</code></pre></div>\n<p>If the form is not valid we call <code>render()</code> again, but this time the form value passed in the context will include error messages.</p>\n<p>If the form is valid, then we can start to use the data, accessing it through the <code>form.cleaned_data</code> attribute (e.g. <code>data = form.cleaned_data['renewal_date']</code>). Here, we just save the data into the <code>due_back</code> value of the associated <code>BookInstance</code> object.</p>\n<div class=\"notecard warning\">\n<p><strong>Warning:</strong>\nWhile you can also access the form data directly through the request (for example, <code>request.POST['renewal_date']</code> or <code>request.GET['renewal_date']</code> if using a GET request), this is NOT recommended. The cleaned data is sanitized, validated, and converted into Python-friendly types.</p>\n</div>\n<p>The final step in the form-handling part of the view is to redirect to another page, usually a \"success\" page. In this case, we use <code>HttpResponseRedirect</code> and <code>reverse()</code> to redirect to the view named <code>'all-borrowed'</code> (this was created as the \"challenge\" in <a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Authentication#challenge_yourself\">Django Tutorial Part 8: User authentication and permissions</a>). If you didn't create that page consider redirecting to the home page at URL <code>/</code>).</p>\n<p>That's everything needed for the form handling itself, but we still need to restrict access to the view to just logged-in librarians who have permission to renew books. We use <code>@login_required</code> to require that the user is logged in, and the <code>@permission_required</code> function decorator with our existing <code>can_mark_returned</code> permission to allow access (decorators are processed in order). Note that we probably should have created a new permission setting in <code>BookInstance</code> (<code>can_renew</code>), but we will reuse the existing one to keep the example simple.</p>\n<p>The final view is therefore as shown below. Please copy this into the bottom of <strong>django-locallibrary-tutorial/catalog/views.py</strong>.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>import datetime\n\nfrom django.contrib.auth.decorators import login_required, permission_required\nfrom django.shortcuts import get_object_or_404\nfrom django.http import HttpResponseRedirect\nfrom django.urls import reverse\n\nfrom catalog.forms import RenewBookForm\n\n@login_required\n@permission_required('catalog.can_mark_returned', raise_exception=True)\ndef renew_book_librarian(request, pk):\n \"\"\"View function for renewing a specific BookInstance by librarian.\"\"\"\n book_instance = get_object_or_404(BookInstance, pk=pk)\n\n # If this is a POST request then process the Form data\n if request.method == 'POST':\n\n # Create a form instance and populate it with data from the request (binding):\n form = RenewBookForm(request.POST)\n\n # Check if the form is valid:\n if form.is_valid():\n # process the data in form.cleaned_data as required (here we just write it to the model due_back field)\n book_instance.due_back = form.cleaned_data['renewal_date']\n book_instance.save()\n\n # redirect to a new URL:\n return HttpResponseRedirect(reverse('all-borrowed'))\n\n # If this is a GET (or any other method) create the default form.\n else:\n proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)\n form = RenewBookForm(initial={'renewal_date': proposed_renewal_date})\n\n context = {\n 'form': form,\n 'book_instance': book_instance,\n }\n\n return render(request, 'catalog/book_renew_librarian.html', context)\n</code></pre></div>"}},{"type":"prose","value":{"id":"the_template","title":"The template","isH3":true,"content":"<p>Create the template referenced in the view (<strong>/catalog/templates/catalog/book_renew_librarian.html</strong>) and copy the code below into it:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">django</span></div><pre class=\"brush: django notranslate\"><code>{% extends \"base_generic.html\" %}\n\n{% block content %}\n &lt;h1&gt;Renew: {{ book_instance.book.title }}&lt;/h1&gt;\n &lt;p&gt;Borrower: {{ book_instance.borrower }}&lt;/p&gt;\n &lt;p {% if book_instance.is_overdue %} class=\"text-danger\"{% endif %} &gt;Due date: {{ book_instance.due_back }}&lt;/p&gt;\n\n &lt;form action=\"\" method=\"post\"&gt;\n {% csrf_token %}\n &lt;table&gt;\n {{ form.as_table }}\n &lt;/table&gt;\n &lt;input type=\"submit\" value=\"Submit\"&gt;\n &lt;/form&gt;\n{% endblock %}\n</code></pre></div>\n<p>Most of this will be completely familiar from previous tutorials.</p>\n<p>We extend the base template and then redefine the content block. We are able to reference <code>{{ book_instance }}</code> (and its variables) because it was passed into the context object in the <code>render()</code> function, and we use these to list the book title, borrower, and the original due date.</p>\n<p>The form code is relatively simple. First, we declare the <code>form</code> tags, specifying where the form is to be submitted (<code>action</code>) and the <code>method</code> for submitting the data (in this case a <code>POST</code>) — if you recall the <a href=\"#html_forms\">HTML Forms</a> overview at the top of the page, an empty <code>action</code> as shown, means that the form data will be posted back to the current URL of the page (which is what we want). Inside the tags, we define the <code>submit</code> input, which a user can press to submit the data. The <code>{% csrf_token %}</code> added just inside the form tags is part of Django's cross-site forgery protection.</p>\n<div class=\"notecard note\">\n<p><strong>Note:</strong>\nAdd the <code>{% csrf_token %}</code> to every Django template you create that uses <code>POST</code> to submit data. This will reduce the chance of forms being hijacked by malicious users.</p>\n</div>\n<p>All that's left is the <code>{{ form }}</code> template variable, which we passed to the template in the context dictionary.\nPerhaps unsurprisingly, when used as shown this provides the default rendering of all the form fields, including their labels, widgets, and help text — the rendering is as shown below:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code>&lt;tr&gt;\n &lt;th&gt;&lt;label for=\"id_renewal_date\"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt;\n &lt;td&gt;\n &lt;input\n id=\"id_renewal_date\"\n name=\"renewal_date\"\n type=\"text\"\n value=\"2023-11-08\"\n required /&gt;\n &lt;br /&gt;\n &lt;span class=\"helptext\"&gt;\n Enter date between now and 4 weeks (default 3 weeks).\n &lt;/span&gt;\n &lt;/td&gt;\n&lt;/tr&gt;\n</code></pre></div>\n<div class=\"notecard note\">\n<p><strong>Note:</strong>\nIt is perhaps not obvious because we only have one field, but, by default, every field is defined in its own table row. This same rendering is provided if you reference the template variable <code>{{ form.as_table }}</code>.</p>\n</div>\n<p>If you were to enter an invalid date, you'd additionally get a list of the errors rendered on the page (see <code>errorlist</code> below).</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code>&lt;tr&gt;\n &lt;th&gt;&lt;label for=\"id_renewal_date\"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt;\n &lt;td&gt;\n &lt;ul class=\"errorlist\"&gt;\n &lt;li&gt;Invalid date - renewal in past&lt;/li&gt;\n &lt;/ul&gt;\n &lt;input\n id=\"id_renewal_date\"\n name=\"renewal_date\"\n type=\"text\"\n value=\"2023-11-08\"\n required /&gt;\n &lt;br /&gt;\n &lt;span class=\"helptext\"&gt;\n Enter date between now and 4 weeks (default 3 weeks).\n &lt;/span&gt;\n &lt;/td&gt;\n&lt;/tr&gt;\n</code></pre></div>\n<h4 id=\"other_ways_of_using_form_template_variable\">Other ways of using form template variable</h4>\n<p>Using <code>{{ form.as_table }}</code> as shown above, each field is rendered as a table row. You can also render each field as a list item (using <code>{{ form.as_ul }}</code>) or as a paragraph (using <code>{{ form.as_p }}</code>).</p>\n<p>It is also possible to have complete control over the rendering of each part of the form, by indexing its properties using dot notation. So, for example, we can access a number of separate items for our <code>renewal_date</code> field:</p>\n<ul>\n<li><code>{{ form.renewal_date }}:</code> The whole field.</li>\n<li><code>{{ form.renewal_date.errors }}</code>: The list of errors.</li>\n<li><code>{{ form.renewal_date.id_for_label }}</code>: The id of the label.</li>\n<li><code>{{ form.renewal_date.help_text }}</code>: The field help text.</li>\n</ul>\n<p>For more examples of how to manually render forms in templates and dynamically loop over template fields, see <a href=\"https://docs.djangoproject.com/en/5.0/topics/forms/#rendering-fields-manually\" class=\"external\" target=\"_blank\">Working with forms &gt; Rendering fields manually</a> (Django docs).</p>"}},{"type":"prose","value":{"id":"testing_the_page","title":"Testing the page","isH3":true,"content":"<p>If you accepted the \"challenge\" in <a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Authentication#challenge_yourself\">Django Tutorial Part 8: User authentication and permissions</a> you'll have a view showing all books on loan in the library, which is only visible to library staff.\nThe view might look similar to this:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">django</span></div><pre class=\"brush: django notranslate\"><code>{% extends \"base_generic.html\" %}\n\n{% block content %}\n &lt;h1&gt;All Borrowed Books&lt;/h1&gt;\n\n {% if bookinstance_list %}\n &lt;ul&gt;\n\n {% for bookinst in bookinstance_list %}\n &lt;li class=\"{% if bookinst.is_overdue %}text-danger{% endif %}\"&gt;\n &lt;a href=\"{% url 'book-detail' bookinst.book.pk %}\"&gt;{{ bookinst.book.title }}&lt;/a&gt; ({{ bookinst.due_back }}) {% if user.is_staff %}- {{ bookinst.borrower }}{% endif %}\n &lt;/li&gt;\n {% endfor %}\n &lt;/ul&gt;\n\n {% else %}\n &lt;p&gt;There are no books borrowed.&lt;/p&gt;\n {% endif %}\n{% endblock %}\n</code></pre></div>\n<p>We can add a link to the book renew page next to each item by appending the following template code to the list item text above.\nNote that this template code can only run inside the <code>{% for %}</code> loop, because that is where the <code>bookinst</code> value is defined.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">django</span></div><pre class=\"brush: django notranslate\"><code>{% if perms.catalog.can_mark_returned %}- &lt;a href=\"{% url 'renew-book-librarian' bookinst.id %}\"&gt;Renew&lt;/a&gt;{% endif %}\n</code></pre></div>\n<div class=\"notecard note\">\n<p><strong>Note:</strong>\nRemember that your test login will need to have the permission <code>catalog.can_mark_returned</code> in order to see the new \"Renew\" link added above, and to access the linked page (perhaps use your superuser account).</p>\n</div>\n<p>You can alternatively manually construct a test URL like this — <code>http://127.0.0.1:8000/catalog/book/&lt;bookinstance_id&gt;/renew/</code> (a valid <code>bookinstance_id</code> can be obtained by navigating to a book detail page in your library, and copying the <code>id</code> field).</p>"}},{"type":"prose","value":{"id":"what_does_it_look_like","title":"What does it look like?","isH3":true,"content":"<p>If you are successful, the default form will look like this:</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_renew_default.png\" alt=\"Default form which displays the book details, due date, renewal date and a submit button appears in case the link works successfully\" width=\"680\" height=\"292\" loading=\"lazy\"></p>\n<p>The form with an invalid value entered will look like this:</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_renew_invalid.png\" alt=\"Same form as above with an error message: invalid date - renewal in the past\" width=\"658\" height=\"290\" loading=\"lazy\"></p>\n<p>The list of all books with renew links will look like this:</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_renew_allbooks.png\" alt=\"Displays list of all renewed books along with their details. Past due is in red.\" width=\"613\" height=\"256\" loading=\"lazy\"></p>"}},{"type":"prose","value":{"id":"modelforms","title":"ModelForms","isH3":false,"content":"<p>Creating a <code>Form</code> class using the approach described above is very flexible, allowing you to create whatever sort of form page you like and associate it with any model or models.</p>\n<p>However, if you just need a form to map the fields of a <em>single</em> model then your model will already define most of the information that you need in your form: fields, labels, help text and so on. Rather than recreating the model definitions in your form, it is easier to use the <a href=\"https://docs.djangoproject.com/en/5.0/topics/forms/modelforms/\" class=\"external\" target=\"_blank\">ModelForm</a> helper class to create the form from your model. This <code>ModelForm</code> can then be used within your views in exactly the same way as an ordinary <code>Form</code>.</p>\n<p>A basic <code>ModelForm</code> containing the same field as our original <code>RenewBookForm</code> is shown below. All you need to do to create the form is add <code>class Meta</code> with the associated <code>model</code> (<code>BookInstance</code>) and a list of the model <code>fields</code> to include in the form.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>from django.forms import ModelForm\n\nfrom catalog.models import BookInstance\n\nclass RenewBookModelForm(ModelForm):\n class Meta:\n model = BookInstance\n fields = ['due_back']\n</code></pre></div>\n<div class=\"notecard note\">\n<p><strong>Note:</strong>\nYou can also include all fields in the form using <code>fields = '__all__'</code>, or you can use <code>exclude</code> (instead of <code>fields</code>) to specify the fields <em>not</em> to include from the model).</p>\n<p>Neither approach is recommended because new fields added to the model are then automatically included in the form (without the developer necessarily considering possible security implications).</p>\n</div>\n<div class=\"notecard note\">\n<p><strong>Note:</strong>\nThis might not look all that much simpler than just using a <code>Form</code> (and it isn't in this case, because we just have one field). However, if you have a lot of fields, it can considerably reduce the amount of code required!</p>\n</div>\n<p>The rest of the information comes from the model field definitions (e.g. labels, widgets, help text, error messages). If these aren't quite right, then we can override them in our <code>class Meta</code>, specifying a dictionary containing the field to change and its new value. For example, in this form, we might want a label for our field of \"<em>Renewal date</em>\" (rather than the default based on the field name: <em>Due Back</em>), and we also want our help text to be specific to this use case.\nThe <code>Meta</code> below shows you how to override these fields, and you can similarly set <code>widgets</code> and <code>error_messages</code> if the defaults aren't sufficient.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>class Meta:\n model = BookInstance\n fields = ['due_back']\n labels = {'due_back': _('New renewal date')}\n help_texts = {'due_back': _('Enter a date between now and 4 weeks (default 3).')}\n</code></pre></div>\n<p>To add validation you can use the same approach as for a normal <code>Form</code> — you define a function named <code>clean_&lt;field_name&gt;()</code> and raise <code>ValidationError</code> exceptions for invalid values.\nThe only difference with respect to our original form is that the model field is named <code>due_back</code> and not <code>renewal_date</code>.\nThis change is necessary since the corresponding field in <code>BookInstance</code> is called <code>due_back</code>.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>from django.forms import ModelForm\n\nfrom catalog.models import BookInstance\n\nclass RenewBookModelForm(ModelForm):\n def clean_due_back(self):\n data = self.cleaned_data['due_back']\n\n # Check if a date is not in the past.\n if data &lt; datetime.date.today():\n raise ValidationError(_('Invalid date - renewal in past'))\n\n # Check if a date is in the allowed range (+4 weeks from today).\n if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):\n raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))\n\n # Remember to always return the cleaned data.\n return data\n\n class Meta:\n model = BookInstance\n fields = ['due_back']\n labels = {'due_back': _('Renewal date')}\n help_texts = {'due_back': _('Enter a date between now and 4 weeks (default 3).')}\n</code></pre></div>\n<p>The class <code>RenewBookModelForm</code> above is now functionally equivalent to our original <code>RenewBookForm</code>. You could import and use it wherever you currently use <code>RenewBookForm</code> as long as you also update the corresponding form variable name from <code>renewal_date</code> to <code>due_back</code> as in the second form declaration: <code>RenewBookModelForm(initial={'due_back': proposed_renewal_date}</code>.</p>"}},{"type":"prose","value":{"id":"generic_editing_views","title":"Generic editing views","isH3":false,"content":"<p>The form handling algorithm we used in our function view example above represents an extremely common pattern in form editing views. Django abstracts much of this \"boilerplate\" for you, by creating <a href=\"https://docs.djangoproject.com/en/5.0/ref/class-based-views/generic-editing/\" class=\"external\" target=\"_blank\">generic editing views</a> for creating, editing, and deleting views based on models. Not only do these handle the \"view\" behavior, but they automatically create the form class (a <code>ModelForm</code>) for you from the model.</p>\n<div class=\"notecard note\">\n<p><strong>Note:</strong>\nIn addition to the editing views described here, there is also a <a href=\"https://docs.djangoproject.com/en/5.0/ref/class-based-views/generic-editing/#formview\" class=\"external\" target=\"_blank\">FormView</a> class, which lies somewhere between our function view and the other generic views in terms of \"flexibility\" vs. \"coding effort\". Using <code>FormView</code>, you still need to create your <code>Form</code>, but you don't have to implement all of the standard form-handling patterns. Instead, you just have to provide an implementation of the function that will be called once the submission is known to be valid.</p>\n</div>\n<p>In this section, we're going to use generic editing views to create pages to add functionality to create, edit, and delete <code>Author</code> records from our library — effectively providing a basic reimplementation of parts of the Admin site (this could be useful if you need to offer admin functionality in a more flexible way than can be provided by the admin site).</p>"}},{"type":"prose","value":{"id":"views","title":"Views","isH3":true,"content":"<p>Open the views file (<strong>django-locallibrary-tutorial/catalog/views.py</strong>) and append the following code block to the bottom of it:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>from django.views.generic.edit import CreateView, UpdateView, DeleteView\nfrom django.urls import reverse_lazy\nfrom .models import Author\n\nclass AuthorCreate(PermissionRequiredMixin, CreateView):\n model = Author\n fields = ['first_name', 'last_name', 'date_of_birth', 'date_of_death']\n initial = {'date_of_death': '11/11/2023'}\n permission_required = 'catalog.add_author'\n\nclass AuthorUpdate(PermissionRequiredMixin, UpdateView):\n model = Author\n # Not recommended (potential security issue if more fields added)\n fields = '__all__'\n permission_required = 'catalog.change_author'\n\nclass AuthorDelete(PermissionRequiredMixin, DeleteView):\n model = Author\n success_url = reverse_lazy('authors')\n permission_required = 'catalog.delete_author'\n\n def form_valid(self, form):\n try:\n self.object.delete()\n return HttpResponseRedirect(self.success_url)\n except Exception as e:\n return HttpResponseRedirect(\n reverse(\"author-delete\", kwargs={\"pk\": self.object.pk})\n )\n</code></pre></div>\n<p>As you can see, to create, update, or delete the views you need to derive from <code>CreateView</code>, <code>UpdateView</code>, and <code>DeleteView</code> (respectively) and then define the associated model.\nWe also restrict calling these views to only logged in users with the <code>add_author</code>, <code>change_author</code>, and <code>delete_author</code> permissions, respectively.</p>\n<p>For the \"create\" and \"update\" cases you also need to specify the fields to display in the form (using the same syntax as for <code>ModelForm</code>). In this case, we show how to list them individually and the syntax to list \"all\" fields. You can also specify initial values for each of the fields using a dictionary of <em>field_name</em>/<em>value</em> pairs (here we arbitrarily set the date of death for demonstration purposes — you might want to remove that). By default, these views will redirect on success to a page displaying the newly created/edited model item, which in our case will be the author detail view we created in a previous tutorial. You can specify an alternative redirect location by explicitly declaring parameter <code>success_url</code>.</p>\n<p>The <code>AuthorDelete</code> class doesn't need to display any of the fields, so these don't need to be specified.\nWe also set a <code>success_url</code> (as shown above), because there is no obvious default URL for Django to navigate to after successfully deleting the <code>Author</code>. Above we use the <a href=\"https://docs.djangoproject.com/en/5.0/ref/urlresolvers/#reverse-lazy\" class=\"external\" target=\"_blank\"><code>reverse_lazy()</code></a> function to redirect to our author list after an author has been deleted — <code>reverse_lazy()</code> is a lazily executed version of <code>reverse()</code>, used here because we're providing a URL to a class-based view attribute.</p>\n<p>If deletion of authors should always succeed that would be it.\nUnfortunately deleting an <code>Author</code> will cause an exception if the author has an associated book, because our <a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Models#book_model\"><code>Book</code> model</a> specifies <code>on_delete=models.RESTRICT</code> for the author <code>ForeignKey</code> field.\nTo handle this case the view overrides the <a href=\"https://docs.djangoproject.com/en/5.0/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin.form_valid\" class=\"external\" target=\"_blank\"><code>form_valid()</code></a> method so that if deleting the <code>Author</code> succeeds it redirects to the <code>success_url</code>, but if not, it just redirects back to the same form.\nWe'll update the template below to make clear that you can't delete an <code>Author</code> instance that is used in any <code>Book</code>.</p>"}},{"type":"prose","value":{"id":"url_configurations","title":"URL configurations","isH3":true,"content":"<p>Open your URL configuration file (<strong>django-locallibrary-tutorial/catalog/urls.py</strong>) and add the following configuration to the bottom of the file:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>urlpatterns += [\n path('author/create/', views.AuthorCreate.as_view(), name='author-create'),\n path('author/&lt;int:pk&gt;/update/', views.AuthorUpdate.as_view(), name='author-update'),\n path('author/&lt;int:pk&gt;/delete/', views.AuthorDelete.as_view(), name='author-delete'),\n]\n</code></pre></div>\n<p>There is nothing particularly new here! You can see that the views are classes, and must hence be called via <code>.as_view()</code>, and you should be able to recognize the URL patterns in each case. We must use <code>pk</code> as the name for our captured primary key value, as this is the parameter name expected by the view classes.</p>"}},{"type":"prose","value":{"id":"templates","title":"Templates","isH3":true,"content":"<p>The \"create\" and \"update\" views use the same template by default, which will be named after your model: <code>model_name_form.html</code> (you can change the suffix to something other than <strong>_form</strong> using the <code>template_name_suffix</code> field in your view, for example, <code>template_name_suffix = '_other_suffix'</code>)</p>\n<p>Create the template file <code>django-locallibrary-tutorial/catalog/templates/catalog/author_form.html</code> and copy the text below.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">django</span></div><pre class=\"brush: django notranslate\"><code>{% extends \"base_generic.html\" %}\n\n{% block content %}\n&lt;form action=\"\" method=\"post\"&gt;\n {% csrf_token %}\n &lt;table&gt;\n {{ form.as_table }}\n &lt;/table&gt;\n &lt;input type=\"submit\" value=\"Submit\" /&gt;\n&lt;/form&gt;\n{% endblock %}\n</code></pre></div>\n<p>This is similar to our previous forms and renders the fields using a table. Note also how again we declare the <code>{% csrf_token %}</code> to ensure that our forms are resistant to CSRF attacks.</p>\n<p>The \"delete\" view expects to find a template named with the format <code>[model_name]_confirm_delete.html</code> (again, you can change the suffix using <code>template_name_suffix</code> in your view).\nCreate the template file <code>django-locallibrary-tutorial/catalog/templates/catalog/author_confirm_delete.html</code> and copy the text below.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">django</span></div><pre class=\"brush: django notranslate\"><code>{% extends \"base_generic.html\" %}\n\n{% block content %}\n\n&lt;h1&gt;Delete Author: {{ author }}&lt;/h1&gt;\n\n{% if author.book_set.all %}\n\n&lt;p&gt;You can't delete this author until all their books have been deleted:&lt;/p&gt;\n&lt;ul&gt;\n {% for book in author.book_set.all %}\n &lt;li&gt;&lt;a href=\"{% url 'book-detail' book.pk %}\"&gt;{{book}}&lt;/a&gt; ({{book.bookinstance_set.all.count}})&lt;/li&gt;\n {% endfor %}\n&lt;/ul&gt;\n\n{% else %}\n&lt;p&gt;Are you sure you want to delete the author?&lt;/p&gt;\n\n&lt;form action=\"\" method=\"POST\"&gt;\n {% csrf_token %}\n &lt;input type=\"submit\" action=\"\" value=\"Yes, delete.\"&gt;\n&lt;/form&gt;\n{% endif %}\n\n{% endblock %}\n</code></pre></div>\n<p>The template should be familiar.\nIt first checks if the author is used in any books, and if so displays the list of books that must be deleted before the author record can be deleted.\nIf not, it displays a form asking the user to confirm they want to delete the author record.</p>\n<p>The final step is to hook the pages into the sidebar.\nFirst we'll add a link for creating the author into the <em>base template</em>, so that it is visible in all pages for logged in users who are considered \"staff\" and who have permission to create authors (<code>catalog.add_author</code>).\nOpen <strong>/django-locallibrary-tutorial/catalog/templates/base_generic.html</strong> and add the lines that allow users with the permission to create the author (in the same block as the link that shows \"All Borrowed\" books).\nRemember to reference the URL using its name <code>'author-create'</code> as shown below.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">django</span></div><pre class=\"brush: django notranslate\"><code>{% if user.is_staff %}\n&lt;hr&gt;\n&lt;ul class=\"sidebar-nav\"&gt;\n&lt;li&gt;Staff&lt;/li&gt;\n &lt;li&gt;&lt;a href=\"{% url 'all-borrowed' %}\"&gt;All borrowed&lt;/a&gt;&lt;/li&gt;\n{% if perms.catalog.add_author %}\n &lt;li&gt;&lt;a href=\"{% url 'author-create' %}\"&gt;Create author&lt;/a&gt;&lt;/li&gt;\n{% endif %}\n&lt;/ul&gt;\n{% endif %}\n</code></pre></div>\n<p>We'll add the links to update and delete authors to the author detail page.\nOpen <strong>catalog/templates/catalog/author_detail.html</strong> and append the following code:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">django</span></div><pre class=\"brush: django notranslate\"><code>{% block sidebar %}\n {{ block.super }}\n\n {% if perms.catalog.change_author or perms.catalog.delete_author %}\n &lt;hr&gt;\n &lt;ul class=\"sidebar-nav\"&gt;\n {% if perms.catalog.change_author %}\n &lt;li&gt;&lt;a href=\"{% url 'author-update' author.id %}\"&gt;Update author&lt;/a&gt;&lt;/li&gt;\n {% endif %}\n {% if not author.book_set.all and perms.catalog.delete_author %}\n &lt;li&gt;&lt;a href=\"{% url 'author-delete' author.id %}\"&gt;Delete author&lt;/a&gt;&lt;/li&gt;\n {% endif %}\n &lt;/ul&gt;\n {% endif %}\n\n{% endblock %}\n</code></pre></div>\n<p>This block overrides the <code>sidebar</code> block in the base template and then pulls in the original content using <code>{{ block.super }}</code>.\nIt then appends links to update or delete the author, but when the user has the correct permissions and the author record isn't associated with any books.</p>\n<p>The pages are now ready to test!</p>"}},{"type":"prose","value":{"id":"testing_the_page_2","title":"Testing the page","isH3":true,"content":"<p>First, log in to the site with an account that has author add, change and delete permissions.</p>\n<p>Navigate to any page, and select \"Create author\" in the sidebar (with URL <code>http://127.0.0.1:8000/catalog/author/create/</code>).\nThe page should look like the screenshot below.</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_create_author.png\" alt=\"Form Example: Create Author\" width=\"676\" height=\"351\" loading=\"lazy\"></p>\n<p>Enter values for the fields and then press <strong>Submit</strong> to save the author record.\nYou should now be taken to a detail view for your new author, with a URL of something like <code>http://127.0.0.1:8000/catalog/author/10</code>.</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_detail_author_update.png\" alt=\"Form Example: Author Detail showing Update and Delete links\" width=\"680\" height=\"427\" loading=\"lazy\"></p>\n<p>You can test editing the record by selecting the \"Update author\" link (with URL something like <code>http://127.0.0.1:8000/catalog/author/10/update/</code>) — we don't show a screenshot because it looks just like the \"create\" page!</p>\n<p>Finally, we can delete the page by selecting \"Delete author\" from the sidebar on the detail page.\nDjango should display the delete page shown below if the author record isn't used in any books.\nPress \"<strong>Yes, delete.</strong>\" to remove the record and be taken to the list of all authors.</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_delete_author.png\" alt=\"Form with option to delete author\" width=\"561\" height=\"194\" loading=\"lazy\"></p>"}},{"type":"prose","value":{"id":"challenge_yourself","title":"Challenge yourself","isH3":false,"content":"<p>Create some forms to create, edit, and delete <code>Book</code> records. You can use exactly the same structure as for <code>Authors</code> (for the deletion, remember that you can't delete a <code>Book</code> until all its associated <code>BookInstance</code> records are deleted) and you must use the correct permissions.\nIf your <strong>book_form.html</strong> template is just a copy-renamed version of the <strong>author_form.html</strong> template, then the new \"create book\" page will look like the screenshot below:</p>\n<p><img src=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms/forms_example_create_book.png\" alt=\"Screenshot displaying various fields in the form like title, author, summary, ISBN, genre and language\" width=\"595\" height=\"521\" loading=\"lazy\"></p>"}},{"type":"prose","value":{"id":"summary","title":"Summary","isH3":false,"content":"<p>Creating and handling forms can be a complicated process! Django makes it much easier by providing programmatic mechanisms to declare, render, and validate forms. Furthermore, Django provides generic form editing views that can do <em>almost all</em> the work to define pages that can create, edit, and delete records associated with a single model instance.</p>\n<p>There is a lot more that can be done with forms (check out our <a href=\"#see_also\">See also</a> list below), but you should now understand how to add basic forms and form-handling code to your own websites.</p>"}},{"type":"prose","value":{"id":"see_also","title":"See also","isH3":false,"content":"<ul>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/topics/forms/\" class=\"external\" target=\"_blank\">Working with forms</a> (Django docs)</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/intro/tutorial04/#write-a-simple-form\" class=\"external\" target=\"_blank\">Writing your first Django app, part 4 &gt; Writing a simple form</a> (Django docs)</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/api/\" class=\"external\" target=\"_blank\">The Forms API</a> (Django docs)</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/fields/\" class=\"external\" target=\"_blank\">Form fields</a> (Django docs)</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/forms/validation/\" class=\"external\" target=\"_blank\">Form and field validation</a> (Django docs)</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/topics/class-based-views/generic-editing/\" class=\"external\" target=\"_blank\">Form handling with class-based views</a> (Django docs)</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/topics/forms/modelforms/\" class=\"external\" target=\"_blank\">Creating forms from models</a> (Django docs)</li>\n<li><a href=\"https://docs.djangoproject.com/en/5.0/ref/class-based-views/generic-editing/\" class=\"external\" target=\"_blank\">Generic editing views</a> (Django docs)</li>\n</ul>\n<p>page(Doc) not found /en-US/docs/Learn_web_development/Extensions/Server-side/Django/authentication_and_sessions</p>"}}],"isActive":true,"isMarkdown":true,"isTranslated":false,"locale":"en-US","mdn_url":"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms","modified":"2024-12-19T15:37:45.000Z","native":"English (US)","noIndexing":false,"other_translations":[{"locale":"de","title":"Django Tutorial Teil 9: Arbeiten mit Formularen","native":"Deutsch"},{"locale":"es","title":"Tutorial de Django Parte 9: Trabajo con formularios","native":"Español"},{"locale":"fr","title":"Django didactique - Section 9 : Travailler avec des formulaires","native":"Français"},{"locale":"ko","title":"Django 튜토리얼 파트 9: 폼(form)으로 작업하기","native":"한국어"},{"locale":"pt-BR","title":"Tutorial Django Parte 9: Trabalhando com formulários","native":"Português (do Brasil)"},{"locale":"ru","title":"Руководство часть 9: Работа с формами","native":"Русский"},{"locale":"zh-CN","title":"Django 教程 9: 使用表单","native":"中文 (简体)"},{"locale":"zh-TW","title":"Django Tutorial Part 9: Working with forms","native":"正體中文 (繁體)"}],"pageTitle":"Django Tutorial Part 9: Working with forms - Learn web development | MDN","parents":[{"uri":"/en-US/docs/Learn_web_development","title":"Learn"},{"uri":"/en-US/docs/Learn_web_development/Extensions","title":"Extension modules"},{"uri":"/en-US/docs/Learn_web_development/Extensions/Server-side","title":"Server-side website programming"},{"uri":"/en-US/docs/Learn_web_development/Extensions/Server-side/Django","title":"Django Web Framework (Python)"},{"uri":"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms","title":"Django Tutorial Part 9: Working with forms"}],"popularity":null,"short_title":"Django Tutorial Part 9: Working with forms","sidebarHTML":"<ol><li class=\"section\"><a href=\"/en-US/docs/Learn_web_development/Getting_started\">Getting started modules</a></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Getting_started/Environment_setup\">Environment setup</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Installing_software\">Installing basic software</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Browsing_the_web\">Browsing the web</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Code_editors\">Code editors</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Dealing_with_files\">Dealing with files</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Command_line\">Command line crash course</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Getting_started/Your_first_website\">Your first website</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Your_first_website/What_will_your_website_look_like\">What will your website look like?</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Creating_the_content\">HTML: Creating the content</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Styling_the_content\">CSS: Styling the content</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Adding_interactivity\">JavaScript: Adding interactivity</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Publishing_your_website\">Publishing your website</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Getting_started/Web_standards\">Web standards</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Web_standards/How_the_web_works\">How the web works</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Web_standards/The_web_standards_model\">The web standards model</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Web_standards/How_browsers_load_websites\">How browsers load websites</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills\">Soft skills</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Research_and_learning\">Research and learning</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Collaboration_and_teamwork\">Collaboration and teamwork</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Workflows_and_processes\">Workflows and processes</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Job_interviews\">Succeeding in job interviews</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn_web_development/Core\">Core modules</a></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content\">Structuring content with HTML</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Basic_HTML_syntax\">Basic HTML syntax</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata\">What's in the head? Webpage metadata</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Headings_and_paragraphs\">Headings and paragraphs in HTML</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Emphasis_and_importance\">Emphasis and importance</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Lists\">Lists</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Structuring_documents\">Structuring documents</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Advanced_text_features\">Advanced text features</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Creating_links\">Creating links</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Marking_up_a_letter\">Challenge: Marking up a letter</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Structuring_a_page_of_content\">Challenge: Structuring a page of content</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_images\">HTML images</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_video_and_audio\">HTML video and audio</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Mozilla_splash_page\">Challenge: Mozilla splash page</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_table_basics\">HTML table basics</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Table_accessibility\">HTML table accessibility</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Planet_data_table\">Challenge: Structuring a planet data table</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_forms\">Forms and buttons in HTML</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Debugging_HTML\">Debugging HTML</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics\">CSS styling basics</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/What_is_CSS\">What is CSS?</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Getting_started\">Getting started with CSS</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Styling_a_bio_page\">Challenge: Styling a biography page</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Basic_selectors\">Basic CSS selectors</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Attribute_selectors\">Attribute selectors</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Pseudo_classes_and_elements\">Pseudo-classes and pseudo-elements</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Combinators\">Combinators</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Box_model\">The box model</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Handling_conflicts\">Handling conflicts</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units\">CSS values and units</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Sizing\">Sizing items in CSS</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Backgrounds_and_borders\">Backgrounds and borders</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Overflow\">Overflowing content</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Images_media_forms\">Images, media, and form elements</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Tables\">Styling tables</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Debugging_CSS\">Debugging CSS</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Fundamental_CSS_comprehension\">Challenge: Fundamental CSS comprehension</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Fancy_letterheaded_paper\">Challenge: Creating fancy letterheaded paper</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Styling_basics/Cool-looking_box\">Challenge: A cool-looking box</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Core/Text_styling\">CSS text styling</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Core/Text_styling/Fundamentals\">Fundamental text and font styling</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Text_styling/Styling_lists\">Styling lists</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Text_styling/Styling_links\">Styling links</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Text_styling/Web_fonts\">Web fonts</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Text_styling/Typesetting_a_homepage\">Challenge: Typesetting a community school homepage</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Core/CSS_layout\">CSS layout</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Core/CSS_layout/Introduction\">Introduction to CSS layout</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/CSS_layout/Floats\">Floats</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/CSS_layout/Positioning\">Positioning</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/CSS_layout/Flexbox\">Flexbox</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/CSS_layout/Grids\">CSS grid layout</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/CSS_layout/Responsive_Design\">Responsive design</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/CSS_layout/Media_queries\">Media query fundamentals</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/CSS_layout/Fundamental_Layout_Comprehension\">Challenge: Fundamental layout comprehension</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Core/Scripting\">Dynamic scripting with JavaScript</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/What_is_JavaScript\">What is JavaScript?</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/A_first_splash\">A first splash into JavaScript</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/What_went_wrong\">What went wrong? Troubleshooting JavaScript</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Variables\">Storing the information you need — Variables</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Math\">Basic math in JavaScript — numbers and operators</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Strings\">Handling text — strings in JavaScript</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Useful_string_methods\">Useful string methods</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Arrays\">Arrays</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Silly_story_generator\">Challenge: Silly story generator</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Conditionals\">Making decisions in your code — conditionals</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Loops\">Looping code</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Functions\">Functions — reusable blocks of code</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Build_your_own_function\">Build your own function</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Return_values\">Function return values</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Events\">Introduction to events</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Event_bubbling\">Event bubbling</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Image_gallery\">Challenge: Image gallery</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Object_basics\">JavaScript object basics</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/DOM_scripting\">DOM scripting introduction</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Network_requests\">Making network requests with JavaScript</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/JSON\">Working with JSON</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Scripting/Debugging_JavaScript\">Debugging JavaScript and handling errors</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries\">JavaScript frameworks and libraries</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/Introduction\">Introduction to client-side frameworks</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/Main_features\">Framework main features</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_getting_started\">Getting started with React</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_todo_list_beginning\">Beginning our React todo list</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_components\">Componentizing our React app</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_interactivity_events_state\">React interactivity: Events and state</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_interactivity_filtering_conditional_rendering\">React interactivity: Editing, filtering, conditional rendering</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_accessibility\">Accessibility in React</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_resources\">React resources</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Core/Accessibility\">Accessibility</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Core/Accessibility/What_is_accessibility\">What is accessibility?</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Accessibility/Tooling\">Accessibility tooling and assistive technology</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Accessibility/HTML\">HTML: A good basis for accessibility</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Accessibility/CSS_and_JavaScript\">CSS and JavaScript accessibility best practices</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Accessibility/WAI-ARIA_basics\">WAI-ARIA basics</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Accessibility/Multimedia\">Accessible multimedia</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Accessibility/Mobile\">Mobile accessibility</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Accessibility/Accessibility_troubleshooting\">Challenge: Accessibility troubleshooting</a></li></ol></details></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Design_for_developers\">Design for developers</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Version_control\">Version control</a></li><li class=\"section\"><a href=\"/en-US/docs/Learn_web_development/Extensions\">Extension modules</a></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects\">Advanced JavaScript objects</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes\">Object prototypes</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object-oriented_programming\">Object-oriented programming</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Classes_in_JavaScript\">Classes in JavaScript</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_building_practice\">Object building practice</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Adding_bouncing_balls_features\">Challenge: Adding features to our bouncing balls demo</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_APIs\">Client-side web APIs</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Introduction\">Introduction to web APIs</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Video_and_audio_APIs\">Video and Audio APIs</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics\">Drawing graphics</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Client-side_storage\">Client-side storage</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Third_party_APIs\">Third-party APIs</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Async_JS\">Asynchronous JavaScript</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Async_JS/Introducing\">Introducing asynchronous JavaScript</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Async_JS/Promises\">How to use promises</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Async_JS/Implementing_a_promise-based_API\">How to implement a promise-based API</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Async_JS/Introducing_workers\">Introducing workers</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Async_JS/Sequencing_animations\">Challenge: Sequencing animations</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms\">Web forms</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/Your_first_form\">Your first form</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/How_to_structure_a_web_form\">How to structure a web form</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/Basic_native_form_controls\">Basic native form controls</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/HTML5_input_types\">The HTML5 input types</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/Other_form_controls\">Other form controls</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/Styling_web_forms\">Styling web forms</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/Advanced_form_styling\">Advanced form styling</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/UI_pseudo-classes\">UI pseudo-classes</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/Form_validation\">Client-side form validation</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Forms/Sending_and_retrieving_form_data\">Sending form data</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_tools\">Understanding client-side tools</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_tools/Overview\">Client-side tooling overview</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_tools/Package_management\">Package management basics</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_tools/Introducing_complete_toolchain\">Introducing a complete toolchain</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Client-side_tools/Deployment\">Deploying our app</a></li></ol></details></li><li class=\"toggle\"><details open=\"\"><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side\">Server-side websites</a></summary><ol><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps\">Server-side first steps</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Introduction\">Introduction to the server side</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Client-Server_overview\">Client-Server Overview</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Web_frameworks\">Server-side web frameworks</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Website_security\">Website security</a></li></ol></details></li><li class=\"toggle\"><details open=\"\"><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django\">Django web framework (Python)</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Introduction\">Django introduction</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/development_environment\">Setting up a Django development environment</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Tutorial_local_library_website\">Django Tutorial: The Local Library website</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/skeleton_website\">Django Tutorial Part 2: Creating a skeleton website</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Models\">Django Tutorial Part 3: Using models</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Admin_site\">Django Tutorial Part 4: Django admin site</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Home_page\">Django Tutorial Part 5: Creating our home page</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Generic_views\">Django Tutorial Part 6: Generic list and detail views</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Sessions\">Django Tutorial Part 7: Sessions framework</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Authentication\">Django Tutorial Part 8: User authentication and permissions</a></li><li><em><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Forms\" aria-current=\"page\">Django Tutorial Part 9: Working with forms</a></em></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Testing\">Django Tutorial Part 10: Testing a Django web application</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/Deployment\">Django Tutorial Part 11: Deploying Django to production</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/web_application_security\">Django web application security</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Django/django_assessment_blog\">Assessment: DIY Django mini blog</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs\">Express web framework (Node.js)</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction\">Express/Node introduction</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/development_environment\">Setting up a Node development environment</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Tutorial_local_library_website\">Express Tutorial: The Local Library website</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/skeleton_website\">Express Tutorial Part 2: Creating a skeleton website</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/mongoose\">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/routes\">Express Tutorial Part 4: Routes and controllers</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Displaying_data\">Express Tutorial Part 5: Displaying library data</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/forms\">Express Tutorial Part 6: Working with forms</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/deployment\">Express Tutorial Part 7: Deploying to production</a></li></ol></details></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance\">Web performance</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/why_web_performance\">The \"why\" of web performance</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/What_is_web_performance\">What is web performance?</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/Perceived_performance\">Perceived performance</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/Measuring_performance\">Measuring performance</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/Multimedia\">Multimedia: Images</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/video\">Multimedia: video</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/JavaScript\">JavaScript performance optimization</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/HTML\">HTML performance optimization</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/CSS\">CSS performance optimization</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Performance/business_case_for_performance\">The business case for web performance</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Extensions/Testing\">Testing</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Testing/Introduction\">Introduction to cross-browser testing</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Testing/Testing_strategies\">Strategies for carrying out testing</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Testing/HTML_and_CSS\">Handling common HTML and CSS problems</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Testing/Feature_detection\">Implementing feature detection</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Testing/Automated_testing\">Introduction to automated testing</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Testing/Your_own_automation_environment\">Setting up your own test automation environment</a></li></ol></details></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Transform_animate\">Transform and animate CSS</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Security_privacy\">Security and privacy</a></li><li class=\"section\">Further resources</li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Howto\">How to solve common problems</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Howto/Solve_HTML_problems\">Solve common HTML problems</a></li><li><a href=\"/en-US/docs/Learn_web_development/Howto/Solve_CSS_problems\">Solve common CSS problems</a></li><li><a href=\"/en-US/docs/Learn_web_development/Howto/Solve_JavaScript_problems\">Solve common JavaScript problems</a></li><li><a href=\"/en-US/docs/Learn_web_development/Howto/Web_mechanics\">Web mechanics</a></li><li><a href=\"/en-US/docs/Learn_web_development/Howto/Tools_and_setup\">Tools and setup</a></li><li><a href=\"/en-US/docs/Learn_web_development/Howto/Design_and_accessibility\">Design and accessibility</a></li></ol></details></li><li><a href=\"/en-US/docs/Learn_web_development/About\">About</a></li><li><a href=\"/en-US/docs/Learn_web_development/Educators\">Resources for educators</a></li><li><a href=\"/en-US/docs/Learn_web_development/Changelog\">Changelog</a></li></ol>","source":{"folder":"en-us/learn_web_development/extensions/server-side/django/forms","github_url":"https://github.com/mdn/content/blob/main/files/en-us/learn_web_development/extensions/server-side/django/forms/index.md","last_commit_url":"https://github.com/mdn/content/commit/5b20f5f4265f988f80f513db0e4b35c7e0cd70dc","filename":"index.md"},"summary":"In this tutorial, we'll show you how to work with HTML Forms in Django, and, in particular, the easiest way to write forms to create, update, and delete model instances. As part of this demonstration, we'll extend the LocalLibrary website so that librarians can renew books, and create, update, and delete authors using our own forms (rather than using the admin application).","title":"Django Tutorial Part 9: Working with forms","toc":[{"text":"Overview","id":"overview"},{"text":"HTML Forms","id":"html_forms"},{"text":"Django form handling process","id":"django_form_handling_process"},{"text":"Renew-book form using a Form and function view","id":"renew-book_form_using_a_form_and_function_view"},{"text":"ModelForms","id":"modelforms"},{"text":"Generic editing views","id":"generic_editing_views"},{"text":"Challenge yourself","id":"challenge_yourself"},{"text":"Summary","id":"summary"},{"text":"See also","id":"see_also"}],"pageType":"learn-module-chapter"}}</script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10