List 타입인 'Entry' <span> {{ Entry|length }} </span>

<span> {{ Entry|count }} </span>


이런방식으로 쓰면됨. 


출처 : http://stackoverflow.com/questions/1465249/jinja2-get-lengths-of-list

An Introduction to Python’s Flask Framework

\Rating:

An Introduction to Python’s Flask Framework

Tutorial Details
  • Difficulty: Beginner
  • Completion Time: 30 Minutes
  • Reference : Link


Flask is a small and powerful web framework for Python. It’s easy to learn and simple to use, enabling you to build your web app in a short amount of time.

In this article, I’ll show you how to build a simple website, containing two static pages with a small amount of dynamic content. While Flask can be used for building complex, database-driven websites, starting with mostly static pages will be useful to introduce a workflow, which we can then generalize to make more complex pages in the future. Upon completion, you’ll be able to use this sequence of steps to jumpstart your next Flask app.


Flask 설치

Before getting started, we need to install Flask. Because systems vary, things can sporadically go wrong during these steps. If they do, like we all do, just Google the error message or leave a comment describing the problem.

virtualenv 설치

Virtualenv is a useful tool that creates isolated Python development environments where you can do all your development work.

We’ll use virtualenv to install Flask. Virtualenv is a useful tool that creates isolated Python development environments where you can do all your development work. Suppose you come across a new Python library that you’d like to try. If you install it system-wide, there is the risk of messing up other libraries that you might have installed. Instead, use virtualenv to create a sandbox, where you can install and use the library without affecting the rest of your system. You can keep using this sandbox for ongoing development work, or you can simply delete it once you’ve finished using it. Either way, your system remains organized and clutter-free.

It’s possible that your system already has virtualenv. Refer to the command line, and try running:

1
$ virtualenv --version

If you see a version number, you’re good to go and you can skip to this “Install Flask” section. If the command was not found, use easy_install or pip to install virtualenv. If running Linux or Mac OS X, one of the following should work for you:

1
$ sudo easy_install virtualenv

or:

1
$ sudo pip install virtualenv

or:

1
$ sudo apt-get install python-virtualenv

If you don’t have either of these commands installed, there are several tutorials online, which will show you how to install it on your system. If you’re running Windows, follow the “Installation Instructions” on this pageto get easy_install up and running on your computer.

Install Flask

After installing virtualenv, you can create a new isolated development environment, like so:

1
$ virtualenv flaskapp

Here, virtualenv creates a folder, flaskapp/, and sets up a clean copy of Python inside for you to use. It also installs the handy package manager, pip.

Enter your newly created development environment and activate it so you can begin working within it.

1
2
$ cd flaskapp
$ . bin/activate

Now, you can safely install Flask:

1
$ pip install Flask

Setting up the Project Structure

Let’s create a couple of folders and files within flaskapp/ to keep our web app organized.

1
2
3
4
5
6
7
8
9
10
.
.
├── app
│   ├── static
│   │   ├── css
│   │   ├── img
│   │   └── js
│   ├── templates
│   ├── routes.py
│   └── README.md

Within flaskapp/, create a folder, app/, to contain all your files. Inside app/, create a folder static/; this is where we’ll put our web app’s images, CSS, and JavaScript files, so create folders for each of those, as demonstrated above. Additionally, create another folder, templates/, to store the app’s web templates. Create an empty Python file routes.py for the application logic, such as URL routing.

And no project is complete without a helpful description, so create a README.md file as well.

Now, we know where to put our project’s assets, but how does everything connect together? Let’s take a look at “Fig. 1″ below to see the big picture:

Fig. 1

  1. 사용자는 홈페이지에 접속하기 위해 도메인의 Root URL에 요청을 보낸다.
  2. routes.py 가 Python 함수에 URL을 요청한다.
  3. Python 함수가 '/templates' 폴더에서 Web Template을 찾는다.
  4. A web template은 '/static' 폴더에서 이미지, CSS, Javascript와 같은 HTML을 렌더링하는데 필요한 파일을 찾는다.
  5. 렌더링 된 HTML이 routes.py 로 전송된다.
  6. routes.py 가 HTML을 브라우저로 보낸다.

We start with a request issued from a web browser. A user types a URL into the address bar. The request hits routes.py, which has code that maps the URL to a function. The function finds a template in thetemplates/ folder, renders it to HTML, and sends it back to the browser. The function can optionally fetch records from a database and then pass that information on to a web template, but since we’re dealing with mostly static pages in this article, we’ll skip interacting with a database for now.

Now that we know our way around the project structure we set up, let’s get started with making a home page for our web app.


Creating a Home Page

When you write a web app with a couple of pages, it quickly becomes annoying to write the same HTML boilerplate over and over again for each page. Furthermore, what if you need to add a new element to your app, such as a new CSS file? You would have to go into every single page and add it in. This is time consuming and error prone. Wouldn’t it be nice if, instead of repeatedly writing the same HTML boilerplate, you could define your page layout just once, and then use that layout to make new pages with their own content? This is exactly what web templates do!

Web templates are simply text files that contain variables and control flow statements (if..elsefor, etc), and end with an.html or .xml extension.

The variables are replaced with your content, when the web template is evaluated. Web templates remove repetition, separate content from design, and make your application easier to maintain. In other, simpler words, web templates are awesome and you should use them! Flask uses the Jinja2 template engine; let’s see how to use it.

As a first step, we’ll define our page layout in a skeleton HTML document layout.html and put it inside thetemplates/ folder:

app/templates/layout.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
  <head>
    <title>Flask App</title>   
  </head>
  <body>
   
    <header>
      <div class="container">
        <h1 class="logo">Flask App</h1>
      </div>
    </header>
     
    <div class="container">
      {% block content %}
      {% endblock %}
    </div>
     
  </body>
</html>

This is simply a regular HTML file…but what’s going on with the {% block content %}{% endblock %}part? To answer this, let’s create another file home.html:

app/templates/home.html

1
2
3
4
5
6
7
{% extends "layout.html" %}
{% block content %}
  <div class="jumbo">
    <h2>Welcome to the Flask app<h2>
    <h3>This is the home page for the Flask app<h3>
  </div>
{% endblock %}

The file layout.html defines an empty block, named content, that a child template can fill in. The filehome.html is a child template that inherits the markup from layout.html and fills in the “content” block with its own text. In other words, layout.html defines all of the common elements of your site, while each child template customizes it with its own content.

This all sounds cool, but how do we actually see this page? How can we type a URL in the browser and “visit” home.html? Let’s refer back to Fig. 1. We just created the template home.html and placed it in thetemplates/ folder. Now, we need to map a URL to it so we can view it in the browser. Let’s open uproutes.py and do this:

app/routes.py

1
2
3
4
5
6
7
8
9
10
from flask import Flask, render_template
 
app = Flask(__name__)     
 
@app.route('/')
def home():
  return render_template('home.html')
 
if __name__ == '__main__':
  app.run(host='0.0.0.0')

That’s it for routes.py. What did we do?

  1. First. we imported the Flask class and a function render_template.
  2. Next, we created a new instance of the Flask class.
  3. We then mapped the URL / to the function home(). Now, when someone visits this URL, the functionhome() will execute.
  4. The function home() uses the Flask function render_template() to render the home.htmltemplate we just created from the templates/ folder to the browser.
  5. Finally, we use run() to run our app on a local server. We’ll set the debug flag to true, so we can view any applicable error messages if something goes wrong, and so that the local server automatically reloads after we’ve made changes to the code.

We’re finally ready to see the fruits of our labor. Return to the command line, and type:

1
$ python routes.py

Visit http://localhost:5000/ in your favorite web browser.

When we visited http://localhost:5000/routes.py had code in it, which mapped the URL / to the Python function home()home() found the web template home.html in the templates/ folder, rendered it to HTML, and sent it back to the browser, giving us the screen above.

Pretty neat, but this home page is a bit boring, isn’t it? Let’s make it look better by adding some CSS. Create a file, main.css, within static/css/, and add these rules:

static/css/main.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
body {
  margin: 0;
  padding: 0;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  color: #444;
}
 
/*
 * Create dark grey header with a white logo
 */
  
header {
  background-color: #2B2B2B;
  height: 35px;
  width: 100%;
  opacity: .9;
  margin-bottom: 10px;
}
 
header h1.logo {
  margin: 0;
  font-size: 1.7em;
  color: #fff;
  text-transform: uppercase;
  float: left;
}
 
header h1.logo:hover {
  color: #fff;
  text-decoration: none;
}
 
/*
 * Center the body content
 */
  
.container {
  width: 940px;
  margin: 0 auto;
}
 
div.jumbo {
  padding: 10px 0 30px 0;
  background-color: #eeeeee;
  -webkit-border-radius: 6px;
     -moz-border-radius: 6px;
          border-radius: 6px;
}
 
h2 {
  font-size: 3em;
  margin-top: 40px;
  text-align: center;
  letter-spacing: -2px;
}
 
h3 {
  font-size: 1.7em;
  font-weight: 100;
  margin-top: 30px;
  text-align: center;
  letter-spacing: -1px;
  color: #999;
}

Add this stylesheet to the skeleton file layout.html so that the styling applies to all of its child templates by adding this line to its <head> element:

1
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">;

We’re using the Flask function, url_for, to generate a URL path for main.css from the static folder. After adding this line in, layout.html should now look like:

app/templates/layout.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
  <head>
    <title>Flask</title>   
    <strong><link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}"></strong>
  </head>
  <body>
    <header>
      <div class="container">
        <h1 class="logo">Flask App</h1>
      </div>
     </header>
    
    <div class="container">
      {% block content %}
      {% endblock %}
    </div>
  </body>
</html>

Let’s switch back to the browser and refresh the page to view the result of the CSS.

That’s more like it! Now, when we visit http://localhost:5000/routes.py still maps the URL / to the Python function home(), and home() still finds the web template home.html in the templates/ folder. But, since we added the CSS file main.css, the web template home.html looks in static/ to find this asset, before rendering to HTML and being sent back to the browser.

We’ve achieved a lot so far. We started with Fig. 1 by understanding how Flask works, and now we’ve seen how it all plays out, by creating a home page for our web app. Let’s move on and create an About page.


Creating an About Page

In the previous section, we created a web template home.html by extending the skeleton file layout.html. We then mapped the URL / to home.html in routes.py so we could visit it in the browser. We finished things up by adding some styling to make it look pretty. Let’s repeat that process again to create an about page for our web app.

We’ll begin by creating a web template, about.html, and putting it inside the templates/ folder.

app/templates/about.html

1
2
3
4
5
6
{% extends "layout.html" %}
  
{% block content %}
  <h2>About</h2>
  <p>This is an About page for the Intro to Flask article. Don't I look good? Oh stop, you're making me blush.</p>
{% endblock %}

Just like before with home.html, we extend from layout.html, and then fill the content block with our custom content.

In order to visit this page in the browser, we need to map a URL to it. Open up routes.py and add another mapping:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask, render_template
  
app = Flask(__name__)
  
@app.route('/')
def home():
  return render_template('home.html')
  
@app.route('/about')
def about():
  return render_template('about.html')
  
if __name__ == '__main__':
  app.run(host='0.0.0.0')

We mapped the URL /about to the function about(). Now we can open up the browser and go tohttp://localhost:5000/about and check out our newly created page.


Adding Navigation

Most websites have links to their main pages within the header or footer of the document. These links are usually visible across all pages of a website. Let’s open up the skeleton file, layout.html. and add these links so they show up in all of the child templates. Specifically, let’s add a <nav> element inside the <header> element:

app/templates/layout.html

1
2
3
4
5
6
7
8
9
10
11
12
13
...
<header>
  <div class="container">
    <h1 class="logo">Flask App</h1>
    <strong><nav>
      <ul class="menu">
        <li><a href="{{ url_for('home') }}">Home</a></li>
        <li><a href="{{ url_for('about') }}">About</a></li>
      </ul>
    </nav></strong>
  </div>
</header>
...

Once again, we use the Flask function url_for to generate URLs.

Next, add some more style rules to main.css to make these new navigation elements look good:

app/static/css/main.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
 
/*
 * Display navigation links inline
 */
 
.menu {
  float: right;
  margin-top: 8px;
}
 
.menu li {
  display: inline;
}
 
.menu li + li {
  margin-left: 35px;
}
 
.menu li a {
  color: #999;
  text-decoration: none;
}

Finally, open up the browser and refresh http://localhost:5000/ to see our newly added navigation links.


Conclusion

Over the course of this article, we built a simple web app with two, mostly static, pages. In doing so, we learned a workflow that can be used to create more complex websites with dynamic content. Flask is a simple, but powerful framework that enables you to efficiently build web apps. Go ahead – check it out!


Intro to Flask: Adding a Contact Page

\Rating:

Intro to Flask: Adding a Contact Page

Tutorial Details
  • Difficulty: Beginner
  • Completion Time: 1 Hour
  • Reference : Link

In the previous article in this mini-series, we leveraged Flask to build a simple website that contains “Home” and “About” pages using a generalized workflow that we can apply to other Flask-based web apps. In this lesson, I’ll demonstrate how to add a “Contact” page that allow users to send you messages.

The code used in this article can be found on GitHub. Captions, such as Checkpoint: 05_contact_form, mean that you can switch to the branch named “05_contact_form” and review the code at that point in the article.


Flask Extensions

You can find a full list of extensions in the Flask Extension Registry.

Flask doesn’t come with many features off the shelf, making it easy to pick up and learn. There is no object-relational mapper for database interaction or admin interfaces to add and update content. It only offers a small set of functions, two of which we’ve already used — url_for() andrender_template().

Instead of shipping with extra functionality, Flask’s extension model allows you to add functionality as needed. A Flask extension is a package that adds specific functionality to your app. For example, Flask-SQLAlchemy adds database support to your app, whereas Flask-Login adds login/logout support. You can find a full list of extensions in the Flask Extension Registry.

To create a Contact page, we’ll use Flask-WTF to handle and validate form data and Flask-Mail to email the form data to you.


Flask-WTF

Flask-WTF is an exension that handles and validates form data. What does that mean? Look at the following figure:

Fig. 1

  1. A user issues a GET request for a web page that contains a form.
  2. The user fills in the form.
  3. The user clicks the “Send” button, submitting it to the server via a POST request.
  4. The server validates the information.
  5. If one or more fields do not validate, the web page containing the form loads again with a helpful error message, prompting the user to try again.
  6. If all fields validate, the form information is used in the next step in the pipeline.

A contact page will have fields for the user’s name, email, subject, and message. In Flask, we’ll POST the form to a function inside routes.py. This function is called the form handler. We’ll run a few validation checks, and if any of the input does not pass muster, we’ll refresh the page to display a message that describes the error. Once all validation checks pass, we’ll use the form data for the next step: emailing the message to you, the website owner.

Flask extensions are simple, powerful tools that extend the functionality of your Flask-based app.

That’s how form handling and validation works. Now where do we actually define the form? We could write HTML using the<form> element and set its action attribute to a Python script. The Python script would mirror the form in order to capture each form field and validate the form field data. If we use this strategy, however, we’d essentially define the form twice — once for the front-end and once for the back-end.

It would be great to define the form only once: in the Python script. This is exactly what Flask-WTF allows us to do. We’ll define the form just once in a Python script, and then we’ll let Flask-WTF generate the form’s HTML for us. The point of all of this is to separate presentation from content.

Enough chatter. Let’s code.

Creating a Form

As a first step, let’s get back into the isolated development environment we created last time.

1
2
$ cd flaskapp
$ . bin/activate

Now that we’ve entered and activated our development environment, we can safely install Flask-WTF:

1
$ pip install flask-wtf

Let’s now define the form in a Python script. We already have routes.py, which maps URLs to functions. Let’s not clutter it with unrelated code. Instead, create a new file called forms.py, and place it inside theapp/ folder.

app/forms.py

1
2
3
4
5
6
7
8
from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField
 
class ContactForm(Form):
  name = TextField("Name")
  email = TextField("Email")
  subject = TextField("Subject")
  message = TextAreaField("Message")
  submit = SubmitField("Send")

We just created a form. What did we do? First, we imported a few useful classes from Flask-WTF — the base Form class, a text field, a textarea field for multi-line text input, and a submit button. Next, we created a new class named ContactForm, inheriting from the base Form class. Then we created each field that we want to see in the contact form. Instead of writing <input type="text">Name</input> in an HTML file, you write name = TextField("Name").

Using the Form

Now let’s use our form. We want it to appear when a user visits the contact page. In Flask terms, we want the form to show up in a web template and map a URL to that web template so we can visit it in the browser. This means we need to create a new web template and a new URL mapping. Let’s start by creating a new URL mapping.

This is an action-packed section, and it may be a little confusing. But stick with me and we’ll get through it.

As a first step, open routes.py and import our newly created form by adding from forms import ContactForm at the beginning of the script.

app/routes.py

1
2
from flask import Flask, render_template
from forms import ContactForm

You can prevent a CSRF attack by making sure that the form submission originates from your web app.

Next, configure Flask-WTF to handle a security exploit known as cross-site request forgery (CSRF). In a perfect world, your server would only process forms that belong to your web app. In other words, your server would only handle and validate the forms that you created. However, it is possible for an attacker to create a form on his own website, fill it in with malicious information, and submit it to your server. If your server accepts this malicious information, all sorts of bad things can happen next.

You can prevent a CSRF attack by making sure that the form submission originates from your web app. One way to do this is to keep a unique token hidden inside your HTML <form>tag that cannot be guessed by attackers. When the form POSTs to your server, the token is checked first. If the token does not match, your server rejects the form submission and does not touch the form data. If the token matches, the server proceeds with form handling and validation.

Flask-WTF does all of this with an easy one-liner. Just configure Flask-WTF with a secret key, and Flask-WTF takes care of generating and managing unique tokens for your forms.

app/routes.py

1
2
3
4
5
6
from flask import Flask, render_template, request, flash
from forms import ContactForm
 
app = Flask(__name__)
 
app.secret_key = 'development key'

Here in line six, I set the secret key to ‘development key’. Feel free to make yours more complex, longer, and alphanumeric.

Now that we’ve imported and configured our contact form, we can use it in a URL mapping in routes.py. Let’s go ahead and create that URL mapping.

app/routes.py

1
2
3
4
@app.route('/contact')
def contact():
  form = ContactForm()
  return render_template('contact.html', form=form)

Now when someone visits the URL /contact, the function contact() will execute. Inside contact(), we first create a new instance of our contact form in line three and sent it to a web template namedcontact.html in line four. We will create this web template shortly.

We still have some work to do here though. Figure 1 showed that if a GET request is sent to the server, the web page containing the form should be retrieved and loaded in browser. If the server receives a POST request, a function should capture the form field data and check if it’s valid. In Python terms, this logic can be expressed in an if...else statement. There is a Flask class for distinguishing between GET and POST requests, so let’s start by importing that class at the beginning of routes.py and add the if...else logic to the contact() function.

app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask, render_template, request
.
.
.
@app.route('/contact', methods=['GET', 'POST'])
def contact():
  form = ContactForm()
 
  if request.method == 'POST':
    return 'Form posted.'
 
  elif request.method == 'GET':
    return render_template('contact.html', form=form)

We already imported the Flask class and render_template() in the previous article, so here we import one more Flask class named requestrequest determines whether the current HTTP method is a GET or a POST. Next is the if...else logic to the contact() function (lines 9-13).

In the case of a POST request, a string indicating that the form has been posted will be returned.

This string is a temporary placeholder, and we’ll replace it with real code in the final step of this article. Otherwise, if the request uses GET, we return the web template contact.html that contains the form.

The next step is to create the web template contact.html and put it inside the templates/ folder.

app/templates/contact.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% extends "layout.html" %}
 
{% block content %}
  <h2>Contact</h2>
  <form action="{{ url_for('contact') }}" method=post>
    {{ form.hidden_tag() }}
 
    {{ form.name.label }}
    {{ form.name }}
 
    {{ form.email.label }}
    {{ form.email }}
 
    {{ form.subject.label }}
    {{ form.subject }}
 
    {{ form.message.label }}
    {{ form.message }}
 
    {{ form.submit }}
  </form>
{% endblock %}

As with home.html and about.html, the contact.html template extends layout.html and fills the ‘content’ block with its own text. We first specify where to send the form data on submission by setting the<form> element’s action attribute to the contact() function we created in routes.py (line five). Next, we let the Jinja2 template engine generate the bulk of the form for us (lines 6-20). We start by inserting a hidden tag in line six to protect against CSRF exploits. Lastly, we add each label and field of the form.

We are now ready to see the result of all our work. Just type the following:

1
$ python routes.py

Then go to http://localhost:5000/contact in your favorite web browser.

The contact page containing the form has loaded. Fill in the form fields and click the "Send" button. You’ll see a page that looks like this:

Awesome! Form submission is working.

Let’s quickly review everything we did in this section:

  • We type in the URL http://localhost:5000/contact into the browser’s address bar.
  • The GET request hits routes.py, where the URL /contact is mapped to the function contact().
  • The function contact() executes, where a variable named form containing a usable instance of theContactForm class is sent to the web template contact.html.
  • contact.html generates the contact form’s HTML.
  • Rendered HTML is sent back to routes.py.
  • routes.py sends the HTML back to the browser and we see the contact page containing the form.
  • We fill in the contact form and submit it by clicking the “Send” button.
  • The POST request hits routes.py, where the URL /contact is mapped to the function contact().
  • The function contact() executes once more, this time following the if...else control flow for the HTTP POST request.
  • The string 'Form posted.' is sent back to the browser, giving us the screen above.

— Checkpoint: 05_contact_form —

This is cool, but the contact form looks ugly. Let’s make it look better by adding some CSS. Open upmain.css and add these rules:

static/css/main.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/* Contact form */
form label {
  font-size: 1.2em;
  font-weight: bold;
  display: block;
  padding: 10px 0;
}
 
form input#name,
form input#email,
form input#subject {
  width: 400px;
  background-color: #fafafa;
  -webkit-border-radius: 3px;
     -moz-border-radius: 3px;
          border-radius: 3px;
  border: 1px solid #cccccc;
  padding: 5px;
  font-size: 1.1em;
}
 
form textarea#message {
  width: 500px;
  height: 100px;
  background-color: #fafafa;
  -webkit-border-radius: 3px;
     -moz-border-radius: 3px;
          border-radius: 3px;
  border: 1px solid #cccccc;
  margin-bottom: 10px;
  padding: 5px;
  font-size: 1.1em;
}
 
form input#submit {
  display: block;
  -webkit-border-radius: 3px;
     -moz-border-radius: 3px;
          border-radius: 3px;
  border:1px solid #d8d8d8;
  padding: 10px;
  font-weight:bold;
  text-align: center;
  color: #000000;
  background-color: #f4f4f4;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f4f4f4), color-stop(100%, #e5e5e5));
  background-image: -webkit-linear-gradient(top, #f4f4f4, #e5e5e5);
  background-image: -moz-linear-gradient(top, #f4f4f4, #e5e5e5);
  background-image: -ms-linear-gradient(top, #f4f4f4, #e5e5e5);
  background-image: -o-linear-gradient(top, #f4f4f4, #e5e5e5);
  background-image: linear-gradient(top, #f4f4f4, #e5e5e5);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#f4f4f4, endColorstr=#e5e5e5);
}
 
form input#submit:hover{
  cursor: pointer;
  border:1px solid #c1c1c1;
  background-color: #dbdbdb;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#dbdbdb), color-stop(100%, #cccccc));
  background-image: -webkit-linear-gradient(top, #dbdbdb, #cccccc);
  background-image: -moz-linear-gradient(top, #dbdbdb, #cccccc);
  background-image: -ms-linear-gradient(top, #dbdbdb, #cccccc);
  background-image: -o-linear-gradient(top, #dbdbdb, #cccccc);
  background-image: linear-gradient(top, #dbdbdb, #cccccc);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#dbdbdb, endColorstr=#cccccc);
}

Switch back to the browser and refresh http://localhost:5000/contact to see the result of the CSS.

This looks much better. Let’s move on to form validation.

— Checkpoint: 06_contact_styling —

Validating Form Data

A user can now visit the URL /contact and fill in the form. But what happens if the user does not properly fill out the form? We need to validate the user input so that it won’t cause problems in later steps.

Form validation is performed by using form validators. Fortunately, Flask-WTF comes with many useful, built-in validators that we can use right away. We’ll put these validators in the ContactForm class definition in forms.py.

The most basic validator is presence, which simply ensures that all form fields are filled in, so let’s start here.

app/forms.py

1
2
3
4
5
6
7
8
from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators, ValidationError
 
class ContactForm(Form):
  name = TextField("Name",  [validators.Required()])
  email = TextField("Email",  [validators.Required()])
  subject = TextField("Subject",  [validators.Required()])
  message = TextAreaField("Message",  [validators.Required()])
  submit = SubmitField("Send")

We start by importing validators and ValidationError from Flask-WTF. This gives us access to Flask-WTF’s built-in validators. Next we add [validators.Required()] to each form field in order to validate its presence. Notice that this validator is inside a Python list, meaning that we can easily add more validators to this list.

Next, let’s require email addresses to match the pattern user@example.com by adding the Email validator to the email field.

app/forms.py

1
2
3
4
5
6
7
8
from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators, ValidationError
 
class ContactForm(Form):
  name = TextField("Name",  [validators.Required()])
  email = TextField("Email",  [validators.Required(), validators.Email()])
  subject = TextField("Subject",  [validators.Required()])
  message = TextAreaField("Message",  [validators.Required()])
  submit = SubmitField("Send")

That does it for our form validations.

— Checkpoint: 07_form_validations —

Looking back at Figure 1, if any validation check fails, the contact page should reload with an error message so that the user can fix the mistake and try again. This error message must only appear when validation fails and disappear when the mistake has been fixed.

Our next step is to send this sort of temporary error message to the user when validation fails. Flask makes this really easy by using its flash() function. Let’s start by opening routes.py and importing Flask’sflash() function at the beginning of the script.

app/routes.py

1
from flask import Flask, render_template, request, flash

After the contact form POSTs to the server, any validation failure should reload the form with a helpful error message. Otherwise, the input data can be used for future processing. Once again, this logic can be expressed in an if...else statement. Let’s add this if...else logic to the contact() function inside theif request.method == 'POST': block.

app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
@app.route('/contact', methods=['GET', 'POST'])
def contact():
  form = ContactForm()
 
  if request.method == 'POST':
    if form.validate() == False:
      flash('All fields are required.')
      return render_template('contact.html', form=form)
    else:
      return 'Form posted.'
 
  elif request.method == 'GET':
    return render_template('contact.html', form=form)

If any validation check fails, form.validate() will be False. The error message All fields are required will be sent to contact.html. Otherwise, we’ll see the temporary placeholder string Form posted, indicating the form has been successfully submitted.

Next, let’s modify contact.html so that it can receive and display these temporary error messages. See the following block:

1
2
3
{% for message in get_flashed_messages() %}
  <div class="flash">{{ message }}</div>
{% endfor %}

The function get_flashed_messages() pulls all flashed messages and returns them. We then simply display each flashed message by using a Jinja2 for loop. Add this code block to contact.html after<h2>Contact</h2> and before the <form> tag.

app/templates/contact.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{% extends "layout.html" %}
 
{% block content %}
  <h2>Contact</h2>
 
  {% for message in get_flashed_messages() %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
   
  <form action="{{ url_for('contact') }}" method=post>
    {{ form.hidden_tag() }}
 
    {{ form.name.label }}
    {{ form.name }}
 
    {{ form.email.label }}
    {{ form.email }}
 
    {{ form.subject.label }}
    {{ form.subject }}
 
    {{ form.message.label }}
    {{ form.message }}
 
    {{ form.submit }}
  </form>
{% endblock %}

Lastly, let’s add a CSS rule in main.css so that flashed error messages look pretty.

main.css

1
2
3
4
5
6
/* Message flashing */
.flash {
  background-color: #FBB0B0;
  padding: 10px;
  width: 400px;
}

Open your browser and visit http://localhost:5000/contact. Leave all the fields blank and click “Send” to test whether form validation and error message flashing work.

This is sweet! We have successfully sent an error message to our contact form if a validation check fails.

— Checkpoint: 08_error_message_flashing —

But we’re not done; we can actually do a little better. Instead of having one generic error message for all failed validation checks, it would be better to have a specific error message for each failed validation check. For example, if the user forgets to fill in the subject field, a specific error message that says Please enter a subject would be flashed. Likewise, if the user forgets to fill in their name, we’d flash a specific error message that says Please enter your name. We can accomplish this pretty easily, so let’s start by writing our specific error messages inside each validator in forms.py.

app/forms.py

1
2
3
4
5
6
7
8
from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators, ValidationError
 
class ContactForm(Form):
  name = TextField("Name",  [validators.Required("Please enter your name.")])
  email = TextField("Email",  [validators.Required("Please enter your email address."), validators.Email("Please enter your email address.")])
  subject = TextField("Subject",  [validators.Required("Please enter a subject.")])
  message = TextAreaField("Message",  [validators.Required("Please enter a message.")])
  submit = SubmitField("Send")

We simply write specific error messages inside each validator. Next, let’s modify contact.html to receive and display these specific error messages. Earlier, we relied on the function get_flashed_messages() to pull flashed error messages, and looped over them to display them. Let’s replace that block with this one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% for message in form.name.errors %}
  <div class="flash">{{ message }}</div>
{% endfor %}
 
{% for message in form.email.errors %}
  <div class="flash">{{ message }}</div>
{% endfor %}
 
{% for message in form.subject.errors %}
  <div class="flash">{{ message }}</div>
{% endfor %}
 
{% for message in form.message.errors %}
  <div class="flash">{{ message }}</div>
{% endfor %}

Here we use the errors attribute for each form field to pull the specific error messages and loop over them using the Jinja2 for loop to display them.

Putting it all together, contact.html now look like this:

app/templates/contact.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{% extends "layout.html" %}
 
{% block content %}
  <h2>Contact</h2>
 
  {% for message in form.name.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
 
  {% for message in form.email.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
 
  {% for message in form.subject.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
 
  {% for message in form.message.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
   
  <form action="{{ url_for('contact') }}" method=post>
    {{ form.hidden_tag() }}
 
    {{ form.name.label }}
    {{ form.name }}
 
    {{ form.email.label }}
    {{ form.email }}
 
    {{ form.subject.label }}
    {{ form.subject }}
 
    {{ form.message.label }}
    {{ form.message }}
 
    {{ form.submit }}
  </form>
{% endblock %}

Switch back to the browser, go to http://localhost:5000/contact, and click “Send”. Be sure to leave all form fields blank.

Perfect! The user now has helpful error messages if he makes a mistake.

— Checkpoint: 09_specific_message_flashing —

We accomplished a lot in this section. We created a contact form from scratch, learned how to protect against CSRF attacks, distinguished between GET and POST requests, enforced form validations, and flashed specific error messages if necessary. We now need to email the message.


Flask-Mail

Flask-Mail is a Flask exension that enables you to send emails from your Flask app. The steps below are similar to those we took to use Flask-WTF.

Let’s start by installing Flask-Mail.

1
$ pip install flask-mail

Configuring Flask-Mail

Next, lets import Flask-Mail into routes.py and configure it so that we can start using it.

app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flask import Flask, render_template, request, flash
from forms import ContactForm
from flask.ext.mail import Message, Mail
 
mail = Mail()
 
app = Flask(__name__)
 
app.secret_key = 'development key'
 
app.config["MAIL_SERVER"] = "smtp.gmail.com"
app.config["MAIL_PORT"] = 465
app.config["MAIL_USE_SSL"] = True
app.config["MAIL_USERNAME"] = 'contact@example.com'
app.config["MAIL_PASSWORD"] = 'your-password'
 
mail.init_app(app)

First, we import the Message and Mail classes from Flask-Mail (line three). We’ll use the Message class to compose a new email and the Mail class to send the email. Next, we create the mail variable that contain a usable instance of the Mail class (line five).

We then configure Flask-Mail with few SMTP server settings (lines 11-15). I used Gmail’s SMTP server settings here, but you can easily use your favorite email provider. Just search for its SMTP settings and you’ll be set.

For example, if you want to use Yahoo! Mail, just search for “yahoo mail smtp server settings” and update the configuration.

Make sure to enter a real email and password in app.config["MAIL_USERNAME"] andapp.config["MAIL_PASSWORD"], respectively. This will be the account from which you’ll send email.

Finally, we attach mail to our Flask app so that we can start using it (line 17).

You’ve probably seen groups use contact email addresses like contact@example.com orsupport@example.com. If you own your own domain and can create a new contact email address, go ahead and put that email address in app.config["MAIL_USERNAME"]. Otherwise, you can use your personal email address just to see how this works.

Sending an Email

Now that the configuration is complete, let’s compose a new email containing the contact form data and send it. We should only send an email if the form has been submitted and all validation checks pass. This means we need to work inside the if request.method == 'POST': block again. We’ve already added logic inside the if form.validate() == False: block to handle validation failures. If all validation checks pass, form.validate() will be True and the program will enter the else block. Therefore, let’s go ahead and add logic inside the else: block.


app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@app.route('/contact', methods=['GET', 'POST'])
def contact():
  form = ContactForm()
 
  if request.method == 'POST':
    if form.validate() == False:
      flash('All fields are required.')
      return render_template('contact.html', form=form)
    else:
      msg = Message(form.subject.data, sender='contact@example.com', recipients=['your_email@example.com'])
      msg.body = """
      From: %s <%s>
      %s
      """ % (form.name.data, form.email.data, form.message.data)
      mail.send(msg)
 
      return 'Form posted.'
 
  elif request.method == 'GET':
    return render_template('contact.html', form=form)

We start by composing a new message (line 10). The Message class takes a subject line, a “from” address, and a “to” address. We then collect the contact form’s subject field data with form.subject.data and set it as the new message’s subject line. The email will be sent from the account you configured inapp.config["MAIL_USERNAME"], so that’s what we used here for the from address. The email will be sent to your personal email address so that you can receive and respond to new messages.

Next, we write the email itself (lines 11-14). We include the user’s name, email and message. I use Python’s string formatting operator % to format the email. And finally, we use mail.send(msg) to send the email (line 15).

Let’s see if everything works. Visit http://localhost:5000/contact, fill out each field, and click “Send.” If all goes well, you’ll receive a new email from your Flask app.

— Checkpoint: 10_send_email —

Tidying Up

Our penultimate step is to remove the temporary placeholder string 'Form posted.' with a message thanking the user for his feedback. This message should only appear if our application sends the email. Once again, this logic can be expressed in an if...else statement.

When the contact form has been successfully submitted, we’ll send a success flag from routes.py to contact.html.

We’ll place the if...else logic inside contact.html. If the success flag is set to True, we’ll display the thank you message. Otherwise, we’ll display the contact form.

Let’s start in routes.py inside the contact() function. Replace the temporary placeholder line return 'Form posted.' with return render_template('contact.html', success=True) in order to send a success flag to contact.html. The contact() function now looks like this:

app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@app.route('/contact', methods=['GET', 'POST'])
def contact():
  form = ContactForm()
 
  if request.method == 'POST':
    if form.validate() == False:
      flash('All fields are required.')
      return render_template('contact.html', form=form)
    else:
      msg = Message(form.subject.data, sender='contact@example.com', recipients=['your_email@example.com'])
      msg.body = """
      From: %s &lt;%s&gt;
      %s
      """ % (form.name.data, form.email.data, form.message.data)
      mail.send(msg)
 
      return render_template('contact.html', success=True)
 
  elif request.method == 'GET':
    return render_template('contact.html', form=form)

Next open contact.html and add the if...else logic. We’ll use Jinja2′s if...else syntax to make this happen.

app/templates/contact.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{% extends "layout.html" %}
 
{% block content %}
  <h2>Contact</h2>
 
  {% if success %}
    <p>Thank you for your message. We'll get back to you shortly.</p>
 
  {% else %}
 
    {% for message in form.name.errors %}
      <div class="flash">{{ message }}</div>
    {% endfor %}
 
    {% for message in form.email.errors %}
      <div class="flash">{{ message }}</div>
    {% endfor %}
 
    {% for message in form.subject.errors %}
      <div class="flash">{{ message }}</div>
    {% endfor %}
 
    {% for message in form.message.errors %}
      <div class="flash">{{ message }}</div>
    {% endfor %}
 
    <form action="{{ url_for('contact') }}" method=post>
      {{ form.hidden_tag() }}
 
      {{ form.name.label }}
      {{ form.name }}
 
      {{ form.email.label }}
      {{ form.email }}
 
      {{ form.subject.label }}
      {{ form.subject }}
 
      {{ form.message.label }}
      {{ form.message }}
 
      {{ form.submit }}
    </form>
 
  {% endif %}
{% endblock %}

Starting in line six, {% if success %} means that if the success flag we sent from routes.py is set toTrue, then display <p>Thank you for your message. We'll get back to you shortly.</p>. Otherwise, follow the {% else %} branch and display the contact form. Jinja2 syntax asks that we close theif...else statement with {% endif %}, so we include that at the end (line 45).

— Checkpoint: 11_success_message —

Finally, let’s visit http://localhost:5000/contact one more time. Fill in each field and click “Send”.

Our last step is to add a navigation link to the contact page. In the previous article, we added these links tolayout.html inside the <header> element. Let’s also do that for the contact page (line eight).

app/templates/layout.html

1
2
3
4
5
6
7
8
9
10
11
12
<header>
  <div class="container">
    <h1 class="logo">Flask App</h1>
    <nav>
      <ul class="menu">
        <li><a href="{{ url_for('home') }}">Home</a></li>
        <li><a href="{{ url_for('about') }}">About</a></li>
        <li><a href="{{ url_for('contact') }}">Contact</a></li>
      </ul>
    </nav>
  </div>
</header>

— Checkpoint: 12_contact_nav_link —

Open up the browser and refresh http://localhost:5000/ to see the newly added navigation link.


Conclusion

In article, we added a contact page that contains a form to our Flask app. Forms appear in several places in web applications, most notably during sign up and login. This workflow can be adapted to meet those needs. In creating a contact page, we learned how to use Flask extensions.

Flask extensions are simple, powerful tools that extend the functionality of your Flask-based app.

Check out the Flask Extension Registry to explore many more extensions that you can integrate into your app.


$ python hello.py 


에서 이미 사용중이라고 에러가 나오면


$ who

$ skill -kill pts/0 




+ Recent posts