(window.webpackJsonp=window.webpackJsonp||[]).push([[160],{684:function(t,s,a){"use strict";a.r(s);var e=a(56),r=Object(e.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"creating-a-form-with-wtforms"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#creating-a-form-with-wtforms"}},[t._v("#")]),t._v(" Creating a form with WTForms")]),t._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),a("p",[t._v("List of all code changes made in this lecture: "),a("a",{attrs:{href:"https://diff-store.com/diff/section14__06_create_form_with_wtforms",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://diff-store.com/diff/section14__06_create_form_with_wtforms"),a("OutboundLink")],1)])]),t._v(" "),a("h2",{attrs:{id:"dependencies"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#dependencies"}},[t._v("#")]),t._v(" Dependencies")]),t._v(" "),a("p",[t._v("To use WTForms in a Flask application, the easiest thing to do is install two dependencies: "),a("code",[t._v("wtforms")]),t._v(" and "),a("code",[t._v("flask-wtf")]),t._v(".")]),t._v(" "),a("p",[t._v("WTForms will help us define the forms, and Flask-WTF will add extra functionality such as that to deal with CSRF tokens.")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("pip install wtforms\npip install flask-wtf\n")])])]),a("h3",{attrs:{id:"what-is-a-csrf-token"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#what-is-a-csrf-token"}},[t._v("#")]),t._v(" What is a CSRF token?")]),t._v(" "),a("p",[t._v("Cross-Site Request Forgery (CSRF)"),a("sup",{staticClass:"footnote-ref"},[a("a",{attrs:{href:"#fn1",id:"fnref1"}},[t._v("[1]")])]),t._v(" is a web security vulnerability. It allows an attacker to trick the browser into submitting a form that sends data to your application, without the user noticing it.")]),t._v(" "),a("p",[t._v("By including a unique token generated by the Flask server in all requests, it prevents the possibility of a CSRF attack because the attacker will need the server to generate the form, which comes with the whole HTML page and usually requires the user to log-in first.")]),t._v(" "),a("h2",{attrs:{id:"create-a-wtform-class"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#create-a-wtform-class"}},[t._v("#")]),t._v(" Create a WTForm Class")]),t._v(" "),a("p",[t._v("Let's create a new file called "),a("code",[t._v("movie_library/forms.py")]),t._v(". There, we will create a Python class that represents our form.")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" flask_wtf "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" FlaskForm\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MovieForm")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("FlaskForm"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("pass")]),t._v("\n")])])]),a("p",[t._v("Note that the import is from "),a("code",[t._v("flask_wtf")]),t._v(", as that will give the form CSRF protection (as long as the necessary field is included in the rendered HTML, more on that later).")]),t._v(" "),a("h2",{attrs:{id:"define-form-fields"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#define-form-fields"}},[t._v("#")]),t._v(" Define Form Fields")]),t._v(" "),a("p",[t._v("Next, we need to define our form fields. This form is for creating new movies, so it will only include four fields: "),a("code",[t._v("title")]),t._v(", "),a("code",[t._v("director")]),t._v(", "),a("code",[t._v("year")]),t._v(", and "),a("code",[t._v("submit")]),t._v(". The first two will be strings, and the "),a("code",[t._v("year")]),t._v(" will be an integer. The "),a("code",[t._v("submit")]),t._v(" field will be rendered as a button to submit the form.")]),t._v(" "),a("p",[t._v("Giving our fields appropriate types will do two things:")]),t._v(" "),a("ul",[a("li",[t._v("Render the appropriate field type in HTML")]),t._v(" "),a("li",[t._v("Make validation easier")])]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" flask_wtf "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" FlaskForm\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" wtforms "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" IntegerField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" StringField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SubmitField\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MovieForm")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("FlaskForm"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n title "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" StringField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Title"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n director "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" StringField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Director"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n year "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" IntegerField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Year"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n submit "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SubmitField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Add Movie"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("With just this, we can go and render the form and it will work just fine!")]),t._v(" "),a("p",[t._v("However, one of the key benefits of WTForms is how easy it is to include validation in our forms. Let's look at that.")]),t._v(" "),a("h2",{attrs:{id:"data-validation-with-wtforms"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#data-validation-with-wtforms"}},[t._v("#")]),t._v(" Data Validation with WTForms")]),t._v(" "),a("p",[t._v("We will be including two types of validation in this form:")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("InputRequired")]),t._v(", which makes it so the field cannot be empty when submitted")]),t._v(" "),a("li",[a("code",[t._v("NumberRange")]),t._v(", which makes it so the field must have a value between two provided numbers")])]),t._v(" "),a("p",[t._v("WTForms ships with many more validators"),a("sup",{staticClass:"footnote-ref"},[a("a",{attrs:{href:"#fn2",id:"fnref2"}},[t._v("[2]")])]),t._v(" that you can use.")]),t._v(" "),a("p",[t._v("For now though, let's add the validation to our fields:")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" flask_wtf "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" FlaskForm\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" wtforms "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" IntegerField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" StringField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" SubmitField\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" wtforms"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("validators "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" InputRequired"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" NumberRange\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MovieForm")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("FlaskForm"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n title "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" StringField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Title"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" validators"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("InputRequired"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n director "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" StringField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Director"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" validators"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("InputRequired"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n year "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" IntegerField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Year"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n validators"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n InputRequired"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n NumberRange"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("min")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1878")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" message"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Please enter a year in the format YYYY."')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n submit "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" SubmitField"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Add Movie"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("I've made all three input fields required, and I've made the "),a("code",[t._v("year")]),t._v(" field have a minimum value of 1878, since that's when the first movie was released.")]),t._v(" "),a("p",[t._v("A few things to note:")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("validators")]),t._v(" is a keyword argument which takes a list of validator objects.")]),t._v(" "),a("li",[t._v("Each validator object takes an optional "),a("code",[t._v("message")]),t._v(" keyword argument which will be displayed to users if the validation fails at that step.")]),t._v(" "),a("li",[t._v("Validators are validated in the order in which they are defined, so that's why I put "),a("code",[t._v("InputRequired()")]),t._v(" first.")])]),t._v(" "),a("p",[t._v("Now that we've defined our form using WTForms, a few questions still remain!")]),t._v(" "),a("ol",[a("li",[t._v("How do we render this form in our template?")]),t._v(" "),a("li",[t._v("How do we receive form data?")]),t._v(" "),a("li",[t._v("How do we validate the form data?")]),t._v(" "),a("li",[t._v("If validation fails, how do we display the error messages to the user?")])]),t._v(" "),a("p",[t._v("We will tackle all these questions over the next 2 lectures. Let's go!")]),t._v(" "),a("hr",{staticClass:"footnotes-sep"}),t._v(" "),a("section",{staticClass:"footnotes"},[a("ol",{staticClass:"footnotes-list"},[a("li",{staticClass:"footnote-item",attrs:{id:"fn1"}},[a("p",[a("a",{attrs:{href:"https://portswigger.net/web-security/csrf",target:"_blank",rel:"noopener noreferrer"}},[t._v("Cross-site request forgery (PortSwigger)"),a("OutboundLink")],1),t._v(" "),a("a",{staticClass:"footnote-backref",attrs:{href:"#fnref1"}},[t._v("↩︎")])])]),t._v(" "),a("li",{staticClass:"footnote-item",attrs:{id:"fn2"}},[a("p",[a("a",{attrs:{href:"https://wtforms.readthedocs.io/en/3.0.x/validators/#built-in-validators",target:"_blank",rel:"noopener noreferrer"}},[t._v("List of built-in validators (WTForms)"),a("OutboundLink")],1),t._v(" "),a("a",{staticClass:"footnote-backref",attrs:{href:"#fnref2"}},[t._v("↩︎")])])])])])])}),[],!1,null,null,null);s.default=r.exports}}]);