Click here for Flask Tutorial. Build a microblogging service called Flaskr. Sqlite is used as the database.
Create a directory to place the application. The name is arbitrary, but static and templates should be used as they are to match the standard. (Don't forget to pluralize with templates)
[root@cnenyuy5l3c ~]# mkdir flaskr
[root@cnenyuy5l3c ~]# cd flaskr
[root@cnenyuy5l3c flaskr]#
[root@cnenyuy5l3c flaskr]# mkdir templates static
[root@cnenyuy5l3c flaskr]#
[root@cnenyuy5l3c flaskr]# virtualenv env
New python executable in /root/flaskr/env/bin/python2.7
Also creating executable in /root/flaskr/env/bin/python
Installing setuptools, pip, wheel...done.
[root@cnenyuy5l3c flaskr]#
[root@cnenyuy5l3c flaskr]# ls
env static templates
[root@cnenyuy5l3c flaskr]#
Enable virtualenv and install flask there.
[root@cnenyuy5l3c flaskr]# . env/bin/activate
(env) [root@cnenyuy5l3c flaskr]#
(env) [root@cnenyuy5l3c flaskr]# pip install flask
Collecting flask
(Omission)
Successfully installed Jinja2-2.9.5 MarkupSafe-0.23 Werkzeug-0.11.15 click-6.7 flask-0.12 itsdangerous-0.24
(env) [root@cnenyuy5l3c flaskr]#
First, create a schema.
schema.sql
drop table if exists entries;
create table entries (
id integer primary key autoincrement,
title string not null,
text string not null
);
This schema is not streamed directly, but modularized in Python. The following flaskr.py is the main body of the flaskr application, but here only the initialization part of the DB is created.
flaskr.py
import sqlite3
from flask import Flask, request, session, g, redirect, url_for, \
abort, render_template, flash
from contextlib import closing #It wasn't officially here, but it was necessary
# configuration
DATABASE = '/tmp/flaskr.db'
DEBUG = True
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'
# create our little application :)
app = Flask(__name__)
app.config.from_object(__name__)
def connect_db():
return sqlite3.connect(app.config['DATABASE'])
def init_db():
with closing(connect_db()) as db:
with app.open_resource('schema.sql') as f: #Here schema.Run sql
db.cursor().executescript(f.read())
db.commit()
#Various View functions will be added here.
if __name__ == '__main__':
app.run(host='0.0.0.0') #Refer to from other than guest local
from_object is a method that reads all uppercase variables in the target. The target here is name, that is, your own file (flaskr.py). Also, in the original, the last app.run does not specify an option, but this time host = '0.0.0.0' is specified for reference from the host machine. Now that we are ready, create a DB.
(flaskr) [root@localhost flaskr]# python
Python 2.7.12 (default, Jan 4 2017, 08:18:28)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from flaskr import init_db
>>> init_db()
>>>
If you get the following error here, sqlite3-devel is missing when compiling python, so you need to re-install it.
>>> from flaskr import init_db
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "flaskr.py", line 5, in <module>
import sqlite3
File "/opt/local/lib/python2.7/sqlite3/__init__.py", line 24, in <module>
from dbapi2 import *
File "/opt/local/lib/python2.7/sqlite3/dbapi2.py", line 28, in <module>
from _sqlite3 import *
ImportError: No module named _sqlite3
Check the created database for the time being.
(env) [root@cnenyuy5l3c flaskr]# sqlite3 /tmp/flaskr.db
SQLite version 3.6.20
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
entries
sqlite>
sqlite> .schema
CREATE TABLE entries (
id integer primary key autoincrement,
title string not null,
text string not null
);
sqlite> .quit
Add functions from here. The first is the process of creating and closing a connection with the database.
@app.before_request
def before_request():
g.db = connect_db()
@app.after_request
def after_request(response):
g.db.close()
return response
g is a special object provided by flask that stores the connection to the current database. (g only stores information for one request) before_request is called before the request. It has no arguments here.
after_request is called after the request. Here, the connection is closed and the response from the DB is passed to the client. Is it okay to think of this as a cliché? From here, enter the content corresponding to the actual page. Basically, the work of associating a URL with a function called by a route decorator.
@app.route('/') # http://<hostname>:5000/Define a function to be called when accessing(routing)
def show_entries():
cur = g.db.execute('select title, text from entries order by id desc')
entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
return render_template('show_entries.html', entries=entries) # show_entries.Embed entries in HTML called html(rendering)
@app.route('/add', methods=['POST'])
def add_entry():
if not session.get('logged_in'):
abort(401)
g.db.execute('insert into entries (title, text) values (?, ?)',
[request.form['title'], request.form['text']])
g.db.commit()
flash('New entry was successfully posted')
return redirect(url_for('show_entries')) #Redirect if you want to skip to a function instead of rendering()use.
@app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != app.config['USERNAME']:
error = 'Invalid username'
elif request.form['password'] != app.config['PASSWORD']:
error = 'Invalid password'
else:
session['logged_in'] = True
flash('You were logged in')
return redirect(url_for('show_entries'))
return render_template('login.html', error=error)
@app.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were logged out')
return redirect(url_for('show_entries'))
Now that we have created the function part, let's move on to HTML (appearance). In Flask, the original base HTML (often called layout.html) is created and expanded as appropriate.
layout.html
<!doctype html>
<title>Flaskr</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<h1>Flaskr</h1>
<div class=metanav>
{% if not session.logged_in %}
<a href="{{ url_for('login') }}">log in</a>
{% else %}
<a href="{{ url_for('logout') }}">log out</a>
{% endif %}
</div>
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %} <!--This block is replaced on reference-->
</div>
show_entries.html
{% extends "layout.html" %} <!--Now specify the underlying HTML-->
{% block body %} <!-- layout.Replace the html body block with:-->
{% if session.logged_in %}
<form action="{{ url_for('add_entry') }}" method=post class=add-entry>
<dl>
<dt>Title:
<dd><input type=text size=30 name=title>
<dt>Text:
<dd><textarea name=text rows=5 cols=40></textarea>
<dd><input type=submit value=Share>
</dl>
</form>
{% endif %}
<ul class=entries>
{% for entry in entries %}
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
{% else %}
<li><em>Unbelievable. No entries here so far</em>
{% endfor %}
</ul>
{% endblock %}
login.html
{% extends "layout.html" %}
{% block body %}
<h2>Login</h2>
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
<form action="{{ url_for('login') }}" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username>
<dt>Password:
<dd><input type=password name=password>
<dd><input type=submit value=Login>
</dl>
</form>
{% endblock %}
Finally, create a CSS file.
style.css
body { font-family: sans-serif; background: #eee; }
a, h1, h2 { color: #377BA8; }
h1, h2 { font-family: 'Georgia', serif; margin: 0; }
h1 { border-bottom: 2px solid #eee; }
h2 { font-size: 1.2em; }
.page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
padding: 0.8em; background: white; }
.entries { list-style: none; margin: 0; padding: 0; }
.entries li { margin: 0.8em 1.2em; }
.entries li h2 { margin-left: -1em; }
.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
.add-entry dl { font-weight: bold; }
.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
margin-bottom: 1em; background: #fafafa; }
.flash { background: #CEE5F5; padding: 0.5em;
border: 1px solid #AACBE2; }
.error { background: #F0D6D6; padding: 0.5em; }
Place the above files as follows.
If possible, start the application and check the access.
(env) [root@cnenyuy5l3c flaskr]# python flaskr.py
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger pin code: 602-691-323
There seems to be no problem. With this, the minimum basics have been suppressed (should), so I plan to move on to the original from the next time.
Recommended Posts