ちょっとしたWEBアプリを作りたい時に便利そうなフレームワーク。いろいろあるようだが、とりあえずこれを試してみることにしてみた。これが理解できるようになってからメガフレームワーク系に進もう。
http://flask.pocoo.org/ 公式サイト
http://flask.pocoo.org/docs/api/ メソッドの使い方などはこのあたり
http://werkzeug.pocoo.org/docs/ よくこのパッケージの機能もでてくるので見てみること
http://flask.pocoo.org/snippets/ tips的な
http://flask.pocoo.org/ 公式サイト
http://flask.pocoo.org/docs/api/ メソッドの使い方などはこのあたり
http://werkzeug.pocoo.org/docs/ よくこのパッケージの機能もでてくるので見てみること
http://flask.pocoo.org/snippets/ tips的な
pipを使うことにする。3.x系では動かないようなので2.7.xあたりで試すのがよい
pip install Flaskインストールすると何個か依存モジュールが入るようだ
Flask==0.6.1 Jinja2==2.5.5 Werkzeug==0.6.2 distribute==0.6.14 wsgiref==0.1.2
公式サイトに載っているのをほぼそのまま
#!/usr/bin/env python # vim:fileencoding=utf-8 import os import sys from flask import Flask app = Flask(__name__) @app.route("/") def index(): return "こんにちわ" if __name__ == "__main__": app.run()実行すれば127.0.0.1:5000で待ちうけする
とりあえずこうしてみた。はじめはpyファイル1つだけでいいかなと思ってたけどいろいろとあってこうする
. |-- app.py `-- application |-- __init__.py |-- config.py |-- static |-- upload |-- templates `-- index.html
#!/usr/bin/env python # vim:fileencoding=utf-8 import sys import os from application import app app.run(debug=True)
ここにいろいろroutingの指定などを記載する
# vim:fileencoding=utf-8 import os import os.path import sys import pprint from flask import Flask, Markup, url_for, abort, redirect, render_template, session, request, make_response, send_from_directory, jsonify, g, flash, escape from werkzeug import secure_filename __version__ = 1.0 app = Flask(__name__)例に書いてるくらいflaskからimportしておけばまず困らないはずだ
設定関連。http://flask.pocoo.org/docs/config/ objectで定義することにした
class Config(object): DEBUG = False TESTING = False SECRET_KEY = "/S97ojSkTNB2C@2DR5qVXKgKGNyaHvz6evbGwk-X.grmR9LE" SESSION_COOKIE_NAME = "_sid" #UPLOADDIR = os.path.join(app.root_path, "upload") class ProductConfig(Config): pass class DevelopConfig(Config): DEBUG = Trueと記載する
app.config.from_object("application.config.DevelopConfig")で設定を読み込める。環境に応じてProductを指定とかできるようにしておけばいいだろう
from flask import Flask, url_for app = Flask(__name__) @app.route("/") def index(): return "top page" @app.route("/hello") def hello(): return "hello page" @app.route("/user/<user_name>") def user(user_name): return "i am %s" % user_name @app.route("/post/<int:post_id>") def post(post_id): return "post id: %d" % post_id with app.test_request_context(): print(url_for("index")) print(url_for("hello")) print(url_for("user", user_name="melvins")) print(url_for("post", post_id=3))どのようにmappingされているかが出力される
/ /hello /user/melvins /post/3これで実行したい場合はapp.runすればよい
flaskのコンソールログを見てるとよく
$remoteip - - [06/Mar/2011 10:27:06] "GET /favicon.ico HTTP/1.1" 404 -と残っているので
@app.route("/favicon.ico") def favicon(): return app.send_static_file("favicon.ico")と定義しておき、staticディレクトリにfavicon.icoを設置しておけばよい
methodsに定義されているメソッドのみ受け付ける。まあ普通はPOST, GETを意識しておけばよさそう
@app.route("/login", methods=["POST", "GET"]) def login(): # anything to do...
flaskアプリケーション内でグローバルに存在することができる値をここにもっておく。flaskのサンプルそのまま
import sqlite3 from flask import g DATABASE = '/path/to/database.db' def connect_db(): return sqlite3.connect(DATABASE) @app.before_request def before_request(): g.db = connect_db() @app.after_request def after_request(response): g.db.close() return response
200以外で返したい場合など
from flask import Flask, abort @app.route("/not_found") def not_found(): abort(404)
どこかに飛ばす場合
from flask import Flask, url_for, send_file, abort, redirect @app.route("/mentenance") def mentenance(): return "mentenance time" @app.route("/menu")。 def route(): return rediredt(url_for("mentenance"))http://localhost:5000/menu にアクセスすると/mentenanceにリダイレクトする(302)
404の場合は404ページを出したい場合など
@app.errorhandler(404) def page_not_found(error): return "not found, error.code
routingを実行する前のフィルタリング。ログイン後のsessionのチェックとか
@app.before_request def before_request_trigger(): # anything to do..
routingを実行後のフィルタリング。Responseを引数として受け取るので返り値も必ずResponseを返すこと
@app.after_request def after_request_trigger(response): # anything to do.. return response
一連の処理が終わった後の処理。sys.exc_info()[1]を引数としてうけているようだ
@app.teardown_request def after_request_trigger(exc): # anything to do.. return exc
python標準のloggingをwrapperしている
from logging import Formatter, FileHandler handler = FileHandler("/tmp/app.log", encoding="utf8") handler.setFormatter(Formatter("[%(asctime)s] %(levelname)-8s %(message)s", "%Y-%m-%d %H:%M:%S")) app.logger.addHandler(handler)あとは
app.logger.info("info log")のように使う
ファイルをアップロードし、アップロードしたファイルを参照するページにリダイレクト。な例
from flask import Flask, url_for, abort, redirect, render_template, request, send_from_directory from werkzeug import secure_filename @app.route("/upload", methods=["POST"]) def upload(): f = request.files["file"] filename = secure_filename(f.filename) f.save(os.path.join(UPLOADDIR, filename)) return redirect(url_for("view_upload", filename=filename)) @app.route("/view_upload/<path:filename>") def view_upload(filename): return send_from_directory(UPLOADDIR, filename)/uploadに対してファイルをアップロードすると /view_upload/filename にアクセスするとコンテンツがそのまま表示される
content-typeを変更したい場合や、独自のヘッダーをつけたい場合など。make_responseは最後にコンテンツを返す時にも使われているようだ
# make_responseが必要 from flask import Flask, url_for, abort, redirect, render_template, request, make_response, send_from_directory @app.route("/") def index(): data = "hello world" response = make_response(data) response.headers["Content-type"] = "text/plain" response.headers["X-Other-Header"] = "blah.." return response
@app.route("/bake") def bake(): data = "bake cookie!" response = make_response(data) response.set_cookie("cookie_test", value="cookie is baked") return response
requestで取得
@app.route("/take") def take(): cookie = request.cookies value = cookie["cookie_test"] return "cookie value: %s" % value
@app.route("/", methods=["GET", "POST"]) def login_form(): if request.method == "POST" : name = request.form.get("name") password = request.form.get("password") if valid_login(name, password) : session["login"] = { "name": name, "password": password } flash("conguraturation", "success") return redirect(url_for("/login")) else: flash("login failure", "error") return render_template("index.html")
@app.route("/login") def login(): if "login" not in session or !valid_login(session["login"]["name"], session["login"]["password"]) : return "session failure" return render_template("login.html")
sessionのところにもサンプルが少しでているが、sessionの機能をつかっている。テンプレート側でエラーメッセージの表示などに使える。かなり便利
sessionのログイン画面のHTML側で
sessionのログイン画面のHTML側で
<div class="error"> {% for category, message in get_flashed_messages(with_categories=True) %} {# category #} {{ message }} {%- endfor %} </div>とするとflashで設定したメッセージが表示される。ちなみに1回きりのメッセージなので、メッセージを取得した時点でsessionからは消えている
jsonで結果を返したい場合
@app.route("/json") def json(): data = { 'list': ['apple', 'banana'], 'dict': {'kurt': 'nirvana'} } return jsonify(data)content-typeはちゃんとapplication/jsonになっている
テンプレートエンジン。http://jinja.pocoo.org/ がベースとなっている。多機能なので、とりあえず最低限こんだけくらいは
# vim:fileencoding=utf-8 from jinja2 import Template template = Template("Hello {{ name }}!") print(template.render(name=u"こんにちは"))実際はテンプレートファイルを別に準備する
Environment, FileSystemLoaderを使う
# vim:fileencoding=utf-8 from jinja2 import Environment, FileSystemLoader # テンプレートディレクトリをFileSystemLoaderに渡して、Environmentのloaderに指定。でいいようだ tmplpath = "/path/to/templates" env = Environment(loader=FileSystemLoader([tmplpath])) template = env.get_template("test.html") print(template.render(var=True, name='kurt cobain', fruits=["apple", "melon", "banana"], dictionary={'one': 1, "two": 2, "three": 3}))FileSystemLoaderの引数は配列にしておくとテンプレートディレクトリのパスを複数定義できるので、普段からこうしておくほうがよさそう。配列じゃなくてもいいけど。テンプレートは以下
# test.html <html> <head> <title>Jinja2おためし</title> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> </head> <body> <h1>{{ name }}</h1> <h2>listのテスト</h2> {# comment #} {% for fruit in fruits -%} <li>{{ loop.index }} {{ fruit }}</li> {%- endfor %} <h2>dictのテスト</h2> {% for key in dictionary.keys() -%} <li>{{ loop.index }} {{ key }} is {{ dictionary[key] }}</li> {%- endfor %} <h2>ifのテスト</h2> {% if var -%} var is {{ var }} {% else %} var is not defined {%- endif %} </body> </html>他の言語とだいたい同じ
Environment option autoescape
env = Environment(loader=FileSystemLoader([tmplpath]), autoescape=True)
テンプレート内で以下のようにする
{% autoescape false %} <h1>{{ message | e }}!</h1> {% endautoescape %}
renderの時に変数を渡さなくてもテンプレートで使用できるようにする
env.globals['tests'] = "global test value"変数として以外でもいろいろ使い方あると思う
pipeでいろいろ処理することができる
escape: {{ var1 | escape }}<br> striptags: {{ var1 | striptags }}<br> upper: {{ var1 | upper }}<br>http://jinja.pocoo.org/docs/templates/#list-of-bui... あたり参照
たとえば文字列のhash値を出力するようなfilterの場合は
from hashlib import md5 def md5sum(value): m = md5() m.update(value.encode("utf-8")) return m.hexdigest() env.filters['md5sum'] = md5sum使う側では
{{ var1 | md5sum }}とすればよい
{% autoescape false %} <h1>{{ message | e | replace("\n", "<br />\n") }}!</h1> {% endautoescape %}
Markupの外側の値を安全に扱う。ようはescape
template = Template("hello {{ name }}") name = Markup("<strong>kurt</strong>") print(template.render(name=name))これは
hello <strong>kurt</strong>
name = Markup("<strong>%s</strong>") % "<u>kurt</u>"は
<strong><u>kurt</u></strong>Markupが返す文字列に何か操作をしても全てHTMLエスケープした状態になる(文字列連結も)
built-in cacheを使う場合はFileSystemBytecodeCacheを使えばよさそう
from jinja2 import Environment, FileSystemLoader, FileSystemBytecodeCache env = Environment(loader=FileSystemLoader("/path/to/templates")), bytecode_cache=FileSystemBytecodeCache("/path/to/cache")))http://jinja.pocoo.org/docs/api/#bytecode-cache memcachedをバックエンドに使用できるようだ
一番重要な機能の一つだと思う。http://jinja.pocoo.org/docs/templates/#template-in...
base.html
継承したテンプレート
base.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> {% block head %} <link rel="stylesheet" href="style.css" /> <title>{% block title %}{% endblock %} - My Webpage</title> {% endblock %} </head> <body> <div id="content">{% block content %}{% endblock %}</div> <div id="footer"> {% block footer %} © Copyright 2008 by <a href="http://domain.invalid/">you</a>. {% endblock %} </div> </body>
継承したテンプレート
{% extends "base.html" %} {% block title %}Index{% endblock %} {% block head %} {{ super() }} <style type="text/css"> .important { color: #336699; } </style> {% endblock %} {% block content %} <h1>Index</h1> <p class="important">Welcome on my awesome homepage.</p> {% endblock %}実行結果(プログラムからは継承したテンプレートを呼び出す)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <link rel="stylesheet" href="style.css" /> <title>Index - My Webpage</title> <style type="text/css"> .important { color: #336699; } </style> </head> <body> <div id="content"> <h1>Index</h1> <p class="important">Welcome on my awesome homepage.</p> </div> <div id="footer"> © Copyright 2008 by <a href="http://domain.invalid/">you</a>. </div> </body>
http://ymotongpoo.appspot.com/jinja2_ja/api.html#i... とりあえずこのへんをみて理解するところから
contextfilterかどちらかを理解しておけばよさそう
単純なfilterではなく、現在のjinja2の環境情報を取得できると考えておけばよさそう。というのを踏まえて、html escape + 改行を<br>に変換するフィルタをこう書くことができる
単純なfilterではなく、現在のjinja2の環境情報を取得できると考えておけばよさそう。というのを踏まえて、html escape + 改行を<br>に変換するフィルタをこう書くことができる
from jinja2 import Markup, Environment, FileSystemLoader, evalcontextfilter from jinja2.utils import soft_unicode @evalcontextfilter def nl2br(eval_ctx, value): res = soft_unicode(Markup.escape(value)).replace("\n", Markup("<br />\n")) if eval_ctx.autoescape: res = Markup(res) return res env = Environment(loader=FileSystemLoader("."), autoescape=False) env.filters["nl2br"] = nl2brjinja2.util.soft_unicodeでMarkup.escapeの効果を無効にして改行をbrに変換して、autoescapeが有効ならその結果をMarkupで保護する。とするとこの結果が二重escapeされる心配もない。eval_ctxはhttp://jinja.pocoo.org/docs/api/#jinja2.nodes.Eval...のこと
http://jinja.pocoo.org/docs/templates/#macros
htmlで部品的なものを作る場合。だいたい使い方はわかった。
macroを定義。form-helper.htmlとして作成する
macroを呼び出す。template.htmlとする。同じファイル内にmacroを定義することも可能だが、たぶん別で定義すると思うので、こうしてる
htmlで部品的なものを作る場合。だいたい使い方はわかった。
macroを定義。form-helper.htmlとして作成する
{% macro input(name, type="text") -%} <input name="{{ name }}" type="{{ type }}" {% for k in kwargs %}{{ k }}="{{ kwargs[k] }}" {% endfor %} /> {%- endmacro %}
macroを呼び出す。template.htmlとする。同じファイル内にmacroを定義することも可能だが、たぶん別で定義すると思うので、こうしてる
{% import "form-helper.html" as form %} <html> <head> <title>{{ title }}</title> </head> <body> {{ form.input("account", type="text", value="", id="faa") }} </body> </html>
ディレクトリ構成としてFlaskを使用して実際にrouteの定義などをしているファイル(今回の場合でいうとinit.py)と同階層にtemplatesというディレクトリを作成し、その中にテンプレートをおかないといけないようだ
- templates/index.html
<html> <head> <title>hello flask</title> </head> <body> <h1>{{ message }}!</h1> </body> </html>プログラムでは
from flask import Flask, url_for, abort, redirect, render_template @app.route("/") def index(): return render_template("index.html", message="index page")とすればhttp://localhost:5000/ にアクセスするとテンプレートが適用されたHTMLが表示されるはず
理由がなければする必要はないと思うが
os.path.join(app.root_path, "templates")とflask/helper.pyで定義されているようなので
from jinja2 import FileSystemLoader app.jinja_loader = FileSystemLoader("/path/to/my/templatedir")とjinja_loaderに再定義してあげればよい。http://flask.pocoo.org/docs/api/#flask.Flask.jinja...
env.filtersのようなことをする
from hashlib import md5 @app.template_filter("md5sum") def md5sum(value): m = md5() m.update(value.encode("utf-8")) return m.hexdigest()
もちろん可能
from jinja2 import evalcontextfilter from jinja2.utils import soft_unicode @app.template_filter("nl2br") @evalcontextfilter def nl2br(eval_ctx, value): res = soft_unicode(Markup.escape(value)).replace("\n", Markup("<br />\n")) if eval_ctx.autoescape: res = Markup(res) return resテンプレートで
{{ name | nl2br }}とすればよい
http://www.tornadoweb.org/ tornado自身もフレームワークだが、non-blockingなwebサーバだけをflaskで使用する
from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop from application import app http_server = HTTPServer(WSGIContainer(app)) # addressは指定なしだと0.0.0.0 http_server.listen(5000, address="127.0.0.1") IOLoop.instance().start()autoreloadやdebugの方法はまだ調べてないからわからない。http://d.hatena.ne.jp/Ehren/20100301/1267467815がヒントになりそうだけど
tornado.autoreloadを使えばよい
#!/usr/bin/env python # vim: fileencoding=utf-8 import os import sys from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop from tornado.autoreload import start from application import app http_server = HTTPServer(WSGIContainer(app)) http_server.listen(5000) loop = IOLoop.instance() start(loop) loop.start()
tornado付属のtornado.optionsを使う
#!/usr/bin/env python # vim: fileencoding=utf-8 import os import sys from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop from tornado.autoreload import start from tornado.options import parse_command_line, define, options from application import app # port番号を指定できるように定義 define("port", default=5000, type=int, help="run on the given port") parse_command_line() http_server = HTTPServer(WSGIContainer(app)) http_server.listen(options.port) loop = IOLoop.instance() start(loop) loop.start()app.pyを実行時に
./app.py --helpとすると
Usage: ./app.py [OPTIONS] Options: --help show this help information --log_file_max_size max size of log files before rollover --log_file_num_backups number of log files to keep --log_file_prefix=PATH Path prefix for log files. Note that if you are running multiple tornado processes, log_file_prefix must be different for each of them (e.g. include the port number) --log_to_stderr Send log output to stderr (colorized if possible). By default use stderr if --log_file_prefix is not set and no other logging is configured. --logging=info|warning|error|none Set the Python log level. If 'none', tornado won't touch the logging configuration. ./app.py --port run on the given portこのように使えるoptionが表示される。アクセスログもこれで取れるようになる。
|-- app.py |-- application | |-- __init__.py | |-- admin | | |-- __init__.py | | |-- templates | | | `-- login.html | | `-- views.py | |-- frontend | | |-- __init__.py | | |-- templates | | `-- views.py | `-- templates | `-- base.html `-- develop.pyURIの構成的に
- / 公開領域
- /admin 管理者用
http://flask.pocoo.org/docs/patterns/packages/
ちなみに処理内容は自分がわかればいいレベルなので、かなり適当
ちなみに処理内容は自分がわかればいいレベルなので、かなり適当
何も変わらない
#!/usr/bin/env python # vim:fileencoding=utf-8 import sys import os from application import app app.run(host='0.0.0.0')
ここからの構成が重要
# vim:fileencoding=utf-8 import sys import os from flask import Flask from application.admin.views import admin from application.frontend.views import frontend app = Flask(__name__) app.config.from_envvar("FLASK_APP_SETTINGS") app.register_module(admin, url_prefix="/admin") app.register_module(frontend, url_prefix="/")/, /adminごとにモジュールを作成し、register_moduleにmoduleとURIとのmappingを行っている
/ 以下にアクセスしたときに呼び出される
# vim:fileencoding=utf-8 import sys import os from flask import Module frontend = Module(__name__, "frontend") @frontend.route("/") def index(): return "frontend.index"下位にあたるモジュールはroutingの定義はFlaskではなく、Moduleで行う。
/admin 以下にアクセスしたときに呼び出される
# vim:fileencoding=utf-8 import sys import os from flask import Module, Flask, url_for, render_template, request admin = Module(__name__, "admin") app = Flask(__name__) app.config.from_envvar("FLASK_APP_SETTINGS") @admin.route("/") def index(): return "admin.index" @admin.route("/login", methods=["POST", "GET"]) def login(): return render_template("admin/login.html") @admin.route("/logout") def logout(): return "admin.logout"render_templateはapplication/admin/templates/login.htmlが呼び出される。中身はこんなかんじ
{% extends "base.html" %} {% block title %}login{% endblock %} {% block head %} {{ super() }} <style type="text/css"> .important { color: #336699; } </style> {% endblock %} {% block content %} <h1>login</h1> <p class="important">Welcome on my awesome homepage.</p> <form action="/admin/login" method="post"> account:<input type="text" name="account" /><br/> password:<input type="password" name="password" /><br/> <input type="submit" /> </form> {% endblock %}PATHの指定しなければapplication/templates配下のテンプレートが呼び出されるようだ。
sqlalchemy plugin http://packages.python.org/Flask-SQLAlchemy/
軽く使ってみた限り、標準のsqlalchemyより使いやすい感じ
軽く使ってみた限り、標準のsqlalchemyより使いやすい感じ
. |-- app.py `-- application |-- __init__.py |-- config.py |-- db.py |-- static | `-- favicon.ico `-- templates `-- index.html
#!/usr/bin/env python # vim:fileencoding=utf-8 import sys import os from application import app app.run(host='0.0.0.0')
sqlalchemy用の設定を追加
class Config(object): DEBUG = False TESTING = False SECRET_KEY = "/S97ojSkTNB2C@2DR5qVXKgKGNyaHvz6evbGwk-X.grmR9LE" SESSION_COOKIE_NAME = "_sid" SQLALCHEMY_DATABASE_URI = "$dbtype://$user:$pass@$server:$port/$dbname?charset=utf8" SQLALCHEMY_ECHO=True class ProductConfig(Config): pass class DevelopConfig(Config): DEBUG = True TESTING = True
Modelになるクラスの定義を行う
# vim:fileencoding=utf-8 import sys import os from flask import Flask from flaskext.sqlalchemy import SQLAlchemy app = Flask(__name__) app.config.from_object("application.config.DevelopConfig") db = SQLAlchemy(app, session_options={"autocommit": True}) class Sections(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128), nullable=False) def __init__(self, id, name): self.id = id self.name = name def __repr__(self): return "<Sections %s %s>" % (self.id, self.name) class Members(db.Model): id = db.Column(db.Integer, primary_key=True) first_name = db.Column(db.String(128), nullable=False) last_name = db.Column(db.String(128), nullable=False) age = db.Column(db.Integer, nullable=False) section_id = db.Column(db.Integer, db.ForeignKey("sections.id")) section = db.relationship("Sections", backref=db.backref("members")) def __init__(self, id, first_name, last_name, age, section_id): self.id = id self.first_name = first_name self.last_name = last_name self.age = age self.section_id = section_id def __repr__(self): return "<Members %s %s %s %s %s>" % (self.id, self.first_name, self.last_name, self.age, self.section_id)
とりあえず一覧表示、登録、削除まで適当に実装
# vim:fileencoding=utf-8 import os import os.path import sys import pprint from flask import Flask, Markup, url_for, abort, redirect, render_template, session, request, make_response, send_from_directory, jsonify, g, flash, escape from application.db import db,Sections, Members __version__ = 1.0 app = Flask(__name__) app.config.from_object("application.config.DevelopConfig") @app.before_request def before_request_trigger(): pass @app.teardown_request def after_request_trigger(exc): return exc @app.route("/") def index(): members = Members.query.order_by(db.desc(Members.id)).all() sections = Sections.query.order_by(Sections.id).all() return render_template("index.html", members=members, sections=sections) @app.route("/add", methods=["POST"]) def add(): first_name = request.form.get("first_name") last_name = request.form.get("last_name") age = request.form.get("age") section_id = request.form.get("section_id") try: member = Members(None, first_name, last_name, age, section_id) # session_makerでできないからここでしてみる。SQLAlchemyをnewするときにできたのでやっぱりやめ # db.session.begin(subtransactions=True) db.session.begin() db.session.add(member) db.session.commit() except Exception, e: db.session.rollback() return repr(e) return redirect(url_for("index")) @app.route("/delete/<int:id>") def delete(id): try: db.session.begin() member = Members.query.get(id) db.session.delete(member) db.session.commit() except Exception, e: db.session.rollback() return repr(e) return redirect(url_for("index")) if __name__ == "__main__": app.run(host='0.0.0.0')
参照画面用。selectの結果をそのままテンプレート変数に割り当て
<html> <head> <title>hello flask</title> </head> <body> <h1>Flask + SQLAlchemy!</h1> <form action="add" method="post"> <table border="1"> <tr> <td>first name</td> <td><input type="text" name="first_name"></td> </tr> <tr> <td>last name</td> <td><input type="text" name="last_name"></td> </tr> <tr> <td>age</td> <td><input type="text" name="age"></td> </tr> <tr> <td>section</td> <td> <select name="section_id"> {% for section in sections %} <option value="{{ section.id }}">{{ section.name }}</option> {% endfor %} </select> </td> </tr> </table> <input type="submit" value="add"> </form> <table border="1"> <tr> <th>id</th> <th>name</th> <th>age</th> <th>section</th> <th>delete</th> </tr> {% for member in members %} <tr> <td>{{ member.id }}</td> <td>{{ member.first_name }} {{ member.last_name }}</td> <td>{{ member.age }}</td> <td>{{ member.section.name }}</td> <td><a href="delete/{{ member.id }}">delete</a></td> </tr> {% endfor %} </body> </html>
タグ
最新コメント