├── .gitignore ├── README.md ├── forms.py ├── routes.py ├── static ├── css │ ├── .gitignore │ └── main.css ├── img │ ├── .gitignore │ └── branch-menu.png └── js │ └── .gitignore └── templates ├── .gitignore ├── about.html ├── contact.html ├── home.html └── layout.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intro to Flask 2 | 3 | This is a sample application for the Intro to Flask series on Nettuts+. 4 | 5 | This repo has the following checkpoints 6 | * [00\_project\_structure](https://github.com/cuttarug/intro-to-flask/tree/00_project_structure) 7 | * [01\_home\_page](https://github.com/cuttarug/intro-to-flask/tree/01_home_page) 8 | * [02\_app\_styling](https://github.com/cuttarug/intro-to-flask/tree/02_app_styling) 9 | * [03\_about\_page](https://github.com/cuttarug/intro-to-flask/tree/03_about_page) 10 | * [04\_nav\_styling](https://github.com/cuttarug/intro-to-flask/tree/04_nav_styling) 11 | * [05\_contact\_form](https://github.com/cuttarug/intro-to-flask/tree/05_contact_form) 12 | * [06_contact_styling](https://github.com/cuttarug/intro-to-flask/tree/06_contact_styling) 13 | * [07\_form\_validations](https://github.com/cuttarug/intro-to-flask/tree/07_form_validations) 14 | * [08\_error\_message\_flashing](https://github.com/cuttarug/intro-to-flask/tree/08_error_message_flashing) 15 | * [09\_specific\_message\_flashing](https://github.com/cuttarug/intro-to-flask/tree/09_specific_message_flashing) 16 | * [10\_send\_email](https://github.com/cuttarug/intro-to-flask/tree/10_send_email) 17 | * [11\_success\_message](https://github.com/cuttarug/intro-to-flask/tree/11_success_message) 18 | * [12\_contact\_nav\_link](https://github.com/cuttarug/intro-to-flask/tree/12_contact_nav_link) 19 | 20 | You can switch to each checkpoint to see what the code looks like up to that point by either cloning the repo or using the GitHub branch menu. 21 | 22 | ## Cloning the repo 23 | First clone this repo by running 24 | ```bash 25 | $ git clone git://github.com/cuttarug/intro-to-flask.git 26 | ``` 27 | 28 | Then fetch each checkpoint to see what the code looks like at that point by running `$ git checkout -b []`. For example, to view the code at checkpoint "05\_contact\_form", type 29 | ```bash 30 | $ git checkout -b 05_contact_form 31 | ``` 32 | 33 | ## Using the GitHub branch menu 34 | Alternatively, you can use GitHub's web interface to view each checkpoint. Click on the branch menu and select the checkpoint you want to view, shown below: 35 | 36 | ![GitHub Branch Menu](https://raw.github.com/cuttarug/intro-to-flask/master/static/img/branch-menu.png) 37 | -------------------------------------------------------------------------------- /forms.py: -------------------------------------------------------------------------------- 1 | from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators, ValidationError 2 | 3 | class ContactForm(Form): 4 | name = TextField("Name", [validators.Required("Please enter your name.")]) 5 | email = TextField("Email", [validators.Required("Please enter your email address."), validators.Email("Please enter your email address.")]) 6 | subject = TextField("Subject", [validators.Required("Please enter a subject.")]) 7 | message = TextAreaField("Message", [validators.Required("Please enter a message.")]) 8 | submit = SubmitField("Send") 9 | -------------------------------------------------------------------------------- /routes.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, flash 2 | from forms import ContactForm 3 | from flask.ext.mail import Message, Mail 4 | 5 | mail = Mail() 6 | 7 | app = Flask(__name__) 8 | 9 | app.secret_key = 'development key' 10 | 11 | app.config["MAIL_SERVER"] = "smtp.gmail.com" 12 | app.config["MAIL_PORT"] = 465 13 | app.config["MAIL_USE_SSL"] = True 14 | app.config["MAIL_USERNAME"] = 'contact@example.com' 15 | app.config["MAIL_PASSWORD"] = 'your-password' 16 | 17 | mail.init_app(app) 18 | 19 | @app.route('/') 20 | def home(): 21 | return render_template('home.html') 22 | 23 | @app.route('/about') 24 | def about(): 25 | return render_template('about.html') 26 | 27 | @app.route('/contact', methods=['GET', 'POST']) 28 | def contact(): 29 | form = ContactForm() 30 | 31 | if request.method == 'POST': 32 | if form.validate() == False: 33 | flash('All fields are required.') 34 | return render_template('contact.html', form=form) 35 | else: 36 | msg = Message(form.subject.data, sender='contact@example.com', recipients=['your_email@example.com']) 37 | msg.body = """ 38 | From: %s <%s> 39 | %s 40 | """ % (form.name.data, form.email.data, form.message.data) 41 | mail.send(msg) 42 | 43 | return render_template('contact.html', success=True) 44 | 45 | elif request.method == 'GET': 46 | return render_template('contact.html', form=form) 47 | 48 | if __name__ == '__main__': 49 | app.run(debug=True) 50 | -------------------------------------------------------------------------------- /static/css/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NETTUTS/intro-to-flask/67c30b421b357b473fa3c2b0fe872b17d435ca4e/static/css/.gitignore -------------------------------------------------------------------------------- /static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 5 | color: #444; 6 | } 7 | 8 | /* Create dark grey header with a white logo */ 9 | 10 | header { 11 | background-color: #2B2B2B; 12 | height: 35px; 13 | width: 100%; 14 | opacity: .9; 15 | margin-bottom: 10px; 16 | } 17 | 18 | header h1.logo { 19 | margin: 0; 20 | font-size: 1.7em; 21 | color: #fff; 22 | text-transform: uppercase; 23 | float: left; 24 | } 25 | 26 | header h1.logo:hover { 27 | color: #fff; 28 | text-decoration: none; 29 | } 30 | 31 | /* Center the body content */ 32 | 33 | .container { 34 | width: 940px; 35 | margin: 0 auto; 36 | } 37 | 38 | div.jumbo { 39 | padding: 10px 0 30px 0; 40 | background-color: #eeeeee; 41 | -webkit-border-radius: 6px; 42 | -moz-border-radius: 6px; 43 | border-radius: 6px; 44 | } 45 | 46 | h2 { 47 | font-size: 3em; 48 | margin-top: 40px; 49 | text-align: center; 50 | letter-spacing: -2px; 51 | } 52 | 53 | h3 { 54 | font-size: 1.7em; 55 | font-weight: 100; 56 | margin-top: 30px; 57 | text-align: center; 58 | letter-spacing: -1px; 59 | color: #999; 60 | } 61 | 62 | /* Display navigation links inline */ 63 | 64 | .menu { 65 | float: right; 66 | margin-top: 8px; 67 | } 68 | 69 | .menu li { 70 | display: inline; 71 | } 72 | 73 | .menu li + li { 74 | margin-left: 35px; 75 | } 76 | 77 | .menu li a { 78 | color: #999; 79 | text-decoration: none; 80 | } 81 | 82 | /* Contact form */ 83 | form label { 84 | font-size: 1.2em; 85 | font-weight: bold; 86 | display: block; 87 | padding: 10px 0; 88 | } 89 | 90 | form input#name, 91 | form input#email, 92 | form input#subject { 93 | width: 400px; 94 | background-color: #fafafa; 95 | -webkit-border-radius: 3px; 96 | -moz-border-radius: 3px; 97 | border-radius: 3px; 98 | border: 1px solid #cccccc; 99 | padding: 5px; 100 | font-size: 1.1em; 101 | } 102 | 103 | form textarea#message { 104 | width: 500px; 105 | height: 100px; 106 | background-color: #fafafa; 107 | -webkit-border-radius: 3px; 108 | -moz-border-radius: 3px; 109 | border-radius: 3px; 110 | border: 1px solid #cccccc; 111 | margin-bottom: 10px; 112 | padding: 5px; 113 | font-size: 1.1em; 114 | } 115 | 116 | form input#submit { 117 | display: block; 118 | -webkit-border-radius: 3px; 119 | -moz-border-radius: 3px; 120 | border-radius: 3px; 121 | border:1px solid #d8d8d8; 122 | padding: 10px; 123 | font-weight:bold; 124 | text-align: center; 125 | color: #000000; 126 | background-color: #f4f4f4; 127 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f4f4f4), color-stop(100%, #e5e5e5)); 128 | background-image: -webkit-linear-gradient(top, #f4f4f4, #e5e5e5); 129 | background-image: -moz-linear-gradient(top, #f4f4f4, #e5e5e5); 130 | background-image: -ms-linear-gradient(top, #f4f4f4, #e5e5e5); 131 | background-image: -o-linear-gradient(top, #f4f4f4, #e5e5e5); 132 | background-image: linear-gradient(top, #f4f4f4, #e5e5e5);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#f4f4f4, endColorstr=#e5e5e5); 133 | } 134 | 135 | form input#submit:hover{ 136 | cursor: pointer; 137 | border:1px solid #c1c1c1; 138 | background-color: #dbdbdb; 139 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#dbdbdb), color-stop(100%, #cccccc)); 140 | background-image: -webkit-linear-gradient(top, #dbdbdb, #cccccc); 141 | background-image: -moz-linear-gradient(top, #dbdbdb, #cccccc); 142 | background-image: -ms-linear-gradient(top, #dbdbdb, #cccccc); 143 | background-image: -o-linear-gradient(top, #dbdbdb, #cccccc); 144 | background-image: linear-gradient(top, #dbdbdb, #cccccc);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#dbdbdb, endColorstr=#cccccc); 145 | } 146 | 147 | /* Message flashing */ 148 | .flash { 149 | background-color: #FBB0B0; 150 | padding: 10px; 151 | width: 400px; 152 | } 153 | -------------------------------------------------------------------------------- /static/img/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NETTUTS/intro-to-flask/67c30b421b357b473fa3c2b0fe872b17d435ca4e/static/img/.gitignore -------------------------------------------------------------------------------- /static/img/branch-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NETTUTS/intro-to-flask/67c30b421b357b473fa3c2b0fe872b17d435ca4e/static/img/branch-menu.png -------------------------------------------------------------------------------- /static/js/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NETTUTS/intro-to-flask/67c30b421b357b473fa3c2b0fe872b17d435ca4e/static/js/.gitignore -------------------------------------------------------------------------------- /templates/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NETTUTS/intro-to-flask/67c30b421b357b473fa3c2b0fe872b17d435ca4e/templates/.gitignore -------------------------------------------------------------------------------- /templates/about.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block content %} 4 |

About

5 |

This is an About page for the Intro to Flask article. Don't I look good? Oh stop, you're making me blush.

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /templates/contact.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block content %} 4 |

Contact

5 | 6 | {% if success %} 7 |

Thank you for your message. We'll get back to you shortly.

8 | 9 | {% else %} 10 | {% for message in form.name.errors %} 11 |
{{ message }}
12 | {% endfor %} 13 | 14 | {% for message in form.email.errors %} 15 |
{{ message }}
16 | {% endfor %} 17 | 18 | {% for message in form.subject.errors %} 19 |
{{ message }}
20 | {% endfor %} 21 | 22 | {% for message in form.message.errors %} 23 |
{{ message }}
24 | {% endfor %} 25 | 26 |
27 | {{ form.hidden_tag() }} 28 | 29 | {{ form.name.label }} 30 | {{ form.name }} 31 | 32 | {{ form.email.label }} 33 | {{ form.email }} 34 | 35 | {{ form.subject.label }} 36 | {{ form.subject }} 37 | 38 | {{ form.message.label }} 39 | {{ form.message }} 40 | 41 | {{ form.submit }} 42 |
43 | 44 | {% endif %} 45 | {% endblock %} 46 | -------------------------------------------------------------------------------- /templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block content %} 3 |
4 |

Welcome to the Flask app

5 |

This is the home page for the Flask app

6 |

7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Flask App 5 | 6 | 7 | 8 | 9 |
10 |
11 |

Flask App

12 | 19 |
20 |
21 | 22 |
23 | {% block content %} 24 | {% endblock %} 25 |
26 | 27 | 28 | 29 | --------------------------------------------------------------------------------