[Python] Flask 基本教學

程式語言:Python
Package:
flask
flask_script
flask_sqlalchemy
flask_admin
flask_login
Flask 官方網站
GitHub

功能:架站

最簡易的程式碼
# hello.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'
運行方法
# linux
#export FLASK_APP=hello.py
# window
set FLASK_APP=hello.py
python -m flask run

Flask-Script
提供外部指令,例:python manage.py runserver --debug
# hello.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'
# manage.py
from flask_script import Manager
from hello import app

manager = Manager(app)

# 自定指令
@manager.command
def hello():
    """Print hello"""
    print("hello")

if __name__ == "__main__":
    manager.run()
運行方法
# 可確認有哪些指令,自定的指令也會出現
python manage.py

# 運行 server
python manage.py runserver

# 運行 server 並啟動 debug,可輸入 pin 直接 debug
python manage.py runserver --debug

# 運行 shell
python manage.py shell

Flask-SQLAlchemy
ORM 工具,可直接用 python 控制資料庫
# hello.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'
    
# -------------- new -----------------
from flask_sqlalchemy import SQLAlchemy

# 指定資料庫位置
db_uri = 'sqlite:///{}'.format('app.db')
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return '<User %r>' % self.username

@app.route('/db_demo')
def db_demo():
    admin = User(username='admin', email='admin@example.com')
    guest = User(username='guest', email='guest@example.com')
    db.session.add(admin)
    db.session.add(guest)
    db.session.commit()
    return User.query.filter_by(username='admin').first().username
# -------------- new -----------------
# manage.py
from flask_script import Manager
from hello import app

manager = Manager(app)

@manager.command
def hello():
    """Print hello"""
    print("hello")

# -------------- new -----------------
from hello import db

@manager.command
def initDB():
    """Initializes the database."""
    db.create_all(bind=None)
# -------------- new -----------------
    
if __name__ == "__main__":
    manager.run()
運行方法
# 建立資料庫
python manage.py initDB

# demo 網址為:http://127.0.0.1:5000/db_demo
python manage.py runserver

Flask-Admin
管理資料庫介面
# hello.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'
     
from flask_sqlalchemy import SQLAlchemy

# 指定資料庫位置
db_uri = 'sqlite:///{}'.format('app.db')
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), nullable=False)
    email = db.Column(db.String(120), nullable=False)

    def __repr__(self):
        return '<User %r>' % self.username
        
@app.route('/db_demo')
def db_demo():
    admin = User(username='admin', email='admin@example.com')
    guest = User(username='guest', email='guest@example.com')
    db.session.add(admin)
    db.session.add(guest)
    db.session.commit()
    return User.query.filter_by(username='admin').first().username
    
# -------------- new -----------------
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView

admin = Admin(app, name='microblog', template_mode='bootstrap3')
admin.add_view(ModelView(User, db.session))
# -------------- new -----------------
# manage.py
from flask_script import Manager
from hello import app

manager = Manager(app)

@manager.command
def hello():
    """Print hello"""
    print("hello")

from hello import db

@manager.command
def initDB():
    """Initializes the database."""
    db.create_all(bind=None)
    
if __name__ == "__main__":
    manager.run()
運行方法
# 介面網址為:http://127.0.0.1:5000/admin/
python manage.py runserver

Flask-Login
提供使用者登入驗證相關工具
# hello.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'
    
from flask_sqlalchemy import SQLAlchemy

# 指定資料庫位置
db_uri = 'sqlite:///{}'.format('app.db')
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), nullable=False)
    email = db.Column(db.String(120), nullable=False)

    def __repr__(self):
        return '<User %r>' % self.username
         
@app.route('/db_demo')
def db_demo():
    admin = User(username='admin', email='admin@example.com')
    guest = User(username='guest', email='guest@example.com')
    db.session.add(admin)
    db.session.add(guest)
    db.session.commit()
    return User.query.filter_by(username='admin').first().username
     
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView

# -------------- new -----------------
from adminView import MyAdminIndexView, Accounts

admin = Admin(app, name='microblog', index_view=MyAdminIndexView(), base_template='backend.html')

import flask_login
class BaseModelView(ModelView):    
    def is_accessible(self):
        return flask_login.current_user.is_authenticated

admin.add_view(BaseModelView(User, db.session))
admin.add_view(BaseModelView(Accounts, db.session))
# -------------- new -----------------
# manage.py
from flask_script import Manager
from hello import app

manager = Manager(app)

@manager.command
def hello():
    """Print hello"""
    print("hello")
 
from hello import db

@manager.command
def initDB():
    """Initializes the database."""
    db.create_all(bind=None)
    
if __name__ == "__main__":
    manager.run()
新增 adminView.py
# adminView.py
from flask import url_for, redirect, request
from wtforms import form, fields, validators
from flask_admin import Admin, AdminIndexView, helpers, expose
from flask_login import LoginManager, current_user, login_user, logout_user
from werkzeug.security import generate_password_hash, check_password_hash

from hello import db, app

# os.urandom(24)
app.config['SECRET_KEY'] = b'\xe1\xa9\x7f9\xa6\x9cwe\xd01\x00\x18\xc2.\xad\x81\xf97Y\xd4wK9P'

# Create user model.
class Accounts(db.Model):
    # 若不寫則看 class name
    __tablename__  = 'accounts'
    # 設定 primary_key
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120))
    password = db.Column(db.String(64))

    # Flask-Login integration
    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False
    
    # for @login_manager.user_loader 使用
    def get_id(self):
        return self.id

    def __repr__(self):
        return '<Accounts {}>'.format(self.username)

# 登入表格
class LoginForm(form.Form):
    username = fields.TextField(validators=[validators.required()])
    password = fields.PasswordField(validators=[validators.required()])

    def validate_username(self, field):
        user = self.get_user()

        if user is None:
            raise validators.ValidationError('Invalid user')

        # we're comparing the plaintext pw with the the hash from the db
        if not check_password_hash(user.password, self.password.data):
        # to compare plain text passwords use
        # if user.password != self.password.data:
            raise validators.ValidationError('Invalid password')

    def get_user(self):
        return db.session.query(Accounts).filter_by(username=self.username.data).first()

# 註冊表格
class RegistrationForm(form.Form):
    username = fields.TextField(validators=[validators.required()])
    email = fields.TextField()
    password = fields.PasswordField('New Password', [validators.DataRequired(),
                                                     validators.EqualTo('confirm', message='Passwords must match')
                                                    ])
    confirm = fields.PasswordField('Repeat Password')
    
    def validate_username(self, field):
        if db.session.query(Accounts).filter_by(username=self.username.data).count() > 0:
            raise validators.ValidationError('Duplicate username')

# 初始登入
def init_login():
    login_manager = LoginManager()
    login_manager.init_app(app)

    # Create user loader function
    @login_manager.user_loader
    def load_user(user_id):
        return db.session.query(Accounts).get(user_id)

# 建立 admin 的登入介面
class MyAdminIndexView(AdminIndexView):
    @expose('/')
    def index(self):
        if not current_user.is_authenticated:
            return redirect(url_for('.login_view'))
        return super(MyAdminIndexView, self).index()

    @expose('/login/', methods=('GET', 'POST'))
    def login_view(self):
        # handle user login
        form = LoginForm(request.form)
        if helpers.validate_form_on_submit(form):
            user = form.get_user()
            login_user(user)

        if current_user.is_authenticated:
            return redirect(url_for('.index'))
        link = '<p>Don\'t have an account? <a href="' + url_for('.register_view') + '">Click here to register.</a></p>'
        self._template_args['form'] = form
        self._template_args['link'] = link
        return super(MyAdminIndexView, self).index()

    @expose('/register/', methods=('GET', 'POST'))
    def register_view(self):
        form = RegistrationForm(request.form)
        if helpers.validate_form_on_submit(form):
            user = Accounts()

            form.populate_obj(user)
            # we hash the users password to avoid saving it as plaintext in the db,
            # remove to use plain text:
            user.password = generate_password_hash(form.password.data)

            db.session.add(user)
            db.session.commit()

            login_user(user)
            return redirect(url_for('.index'))
        link = '<p>Already have an account? <a href="' + url_for('.login_view') + '">Click here to log in.</a></p>'
        self._template_args['form'] = form
        self._template_args['link'] = link
        return super(MyAdminIndexView, self).index()

    @expose('/logout/')
    def logout_view(self):
        logout_user()
        return redirect(url_for('.index'))

# Initialize flask-login
init_login()
新增 backend.html
{% extends 'admin/base.html' %}

{% block access_control %}
{% if current_user.is_authenticated %}
<div class="btn-group pull-right">
    <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
        <i class="icon-user"></i> {{ current_user.login }} <span class="caret"></span>
    </a>
    <ul class="dropdown-menu">
        <li><a href="{{ url_for('admin.logout_view') }}">Log out</a></li>
    </ul>
</div>
{% endif %}
{% endblock %}
新增 admin/index.html
{% extends 'admin/master.html' %}
{% block body %}
{{ super() }}
<div class="row-fluid">

    <div>
        {% if current_user.is_authenticated %}
        <p class="lead">
            Authentication
        </p>
        <p>
            {{ current_user }} Example
        </p>
        {% else %}
        <form method="POST" action="">
            {{ form.hidden_tag() if form.hidden_tag }}
            {% for f in form if f.type != 'CSRFTokenField' %}
            <div>
            {{ f.label }}
            {{ f }}
            {% if f.errors %}
            <ul>
                {% for e in f.errors %}
                <li>{{ e }}</li>
                {% endfor %}
            </ul>
            {% endif %}
            </div>
            {% endfor %}
            <button class="btn" type="submit">Submit</button>
        </form>
        {{ link | safe }}
        {% endif %}
    </div>

    <a class="btn btn-primary" href="/"><i class="icon-arrow-left icon-white"></i> Back</a>
</div>
{% endblock body %}
運行方法
# 介面網址為:http://127.0.0.1:5000/admin/
python manage.py runserver

Flask Blueprints
將 view 額外拆成另一個檔案 view.py,以便管理
# hello.py
from flask import Flask
from view import simple_page
app = Flask(__name__)

app.register_blueprint(simple_page)
# manage.py
from flask_script import Manager
from hello import app

manager = Manager(app)
    
if __name__ == "__main__":
    manager.run()
# view.py
from flask import Blueprint

simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates')

@simple_page.route('/')
def hello_world():
    return 'Hello, World!'

參考

用 Flask 與 SQLite 架抽籤網站

留言