flask是一款非常流行的Python Web框架,出生於2010年,作者是Armin Ronacher,本來這個項目只是作者在愚人節的一個玩笑,後來由於非常受歡迎,進而成為一個正式的項目。
flask自2010年發布第一個版本以來,大受歡迎,深得開發者的喜愛,目前在Github上的Star數已經超過55.5k了,有超Django之趨勢。flask能如此流行的原因,可以分為以下幾點:
Flask的靈活度非常之高,他不會幫你做太多的決策,一些你都可以按照自己的意願進行更改。比如:
在終端輸入命令 pip install flask 即可安裝。
新建項目中框架選擇需pycharm專業版才有這功能
在浏覽器中輸入http://127.0.0.1:5000就能看到hello world了。需要說明一點的是,app.run這種方式只適合於開發,如果在生產環境中,應該使用Gunicorn或者uWSGI來啟動。如果是在終端運行的,可以按ctrl+c來讓服務停止。
默認情況下flask不會開啟DEBUG模式,開啟DEBUG模式後,flask會在每次保存代碼的時候自動的重新載入代碼,此時網頁直接刷新就能看到效果,並且如果代碼有錯誤,會在終端進行提示。
需要注意的是,只能在開發環境下開啟DEBUG模式,因為DEBUG模式會帶來非常大的安全隱患。
Flask項目的配置,都是通過app.config對象來進行配置的。比如要配置一個項目的SECRET_KEY,那麼可以使用app.config[‘SECRET_KEY’] = "xxx"來進行設置
常用有這幾種方法:
1、在py文件中直接硬編碼:
缺點:需要一個個寫,涉及文件多的話顯得很累贅
2、將所有配置項寫成一個配置文件,然後使用者進行模塊導入
Flask項目內置了許多的配置項,所有的內置配置項,可以點這查看~
@app.route()是什麼?
從之前的helloworld.py文件中,我們已經看到,一個URL要與執行函數進行映射,使用的是@app.route裝飾器。@app.route裝飾器中,可以指定URL的規則來進行更加詳細的映射,比如現在要映射一個文章詳情的URL,文章詳情的URL是/article/id/,id有可能為1、2、3…,那麼可以通過以下方式:
@app.route('/article/<id>/')
def article(id):
return '%s article detail' % id
其中,尖括號是固定寫法,語法為,variable默認的數據類型是字符串。如果需要指定類型,則要寫成converter:variable,其中converter就是類型名稱,可以有以下幾種:
@app.route('/<any(article,blog):url_path>/')
def item(url_path):
return url_path
以上例子中,item這個函數可以接受兩個URL,一個是/article/,另一個是/blog/。並且,一定要傳url_path參數,當然這個url_path的名稱可以隨便。
如果不想定制子路徑來傳遞參數,也可以通過傳統的?=的形式來傳遞參數,例如:/article?id=xxx,這種情況下,可以通過request.args.get(‘id’)來獲取id的值。如果是post方法,則可以通過request.form.get(‘id’)來進行獲取
一般我們通過一個URL就可以執行到某一個函數。如果反過來,我們知道一個函數,怎麼去獲得這個URL呢?url_for函數就可以幫我們實現這個功能。url_for()函數接收兩個及以上的參數,他接收函數名作為第一個參數,接收對應URL規則的命名參數,如果還出現其他的參數,則會添加到URL的後面作為查詢參數。
在@app.route()中可以傳入一個關鍵字參數methods來指定本方法支持的HTTP方法,默認情況下,只能使用GET請求,看以下例子:
以上裝飾器將讓login的URL既能支持GET又能支持POST。
重定向分為永久性重定向和暫時性重定向,在頁面上體現的操作就是浏覽器會從一個頁面自動跳轉到另外一個頁面。比如用戶訪問了一個需要權限的頁面,但是該用戶當前並沒有登錄,因此我們應該給他重定向到登錄頁面。
在flask中,重定向是通過flask.redirect(location,code=302)這個函數來實現的,location表示需要重定向到的URL,應該配合之前講的url_for()函數來使用,code表示采用哪個重定向,默認是302也即暫時性重定向,可以修改成301來實現永久性重定向。
兩個例子都為如果沒有登錄或者找不到該用戶,即給你重定向到首頁或者登錄頁面!
模板是一個web開發必備的模塊。因為我們在渲染一個網頁的時候,並不是只渲染一個純文本字符串,而是需要渲染一個有富文本標簽的頁面。這時候我們就需要使用模板了。在Flask中,配套的模板是Jinja2,Jinja2的作者也是Flask的作者。這個模板非常的強大,並且執行效率高。以下對Jinja2做一個簡單介紹!
要渲染一個模板,通過render_template方法即可,以下將用一個簡單的例子進行講解:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/about/')
def about():
return render_template('about.html')
當訪問/about/的時候,about()函數會在當前目錄下的templates(默認不建議修改)文件夾下尋找about.html模板文件。如果想更改模板文件地址,應該在創建app的時候,給Flask傳遞一個關鍵字參數template_folder,指定具體的路徑,再看以下例子:
from flask import Flask,render_template
app = Flask(__name__,template_folder=r'C:\templates')
@app.route('/about/')
def about():
return render_template('about.html')
以上例子將會在C盤的templates文件夾中尋找模板文件。還有最後一點是,如果模板文件中有參數需要傳遞,應該怎麼傳呢,我們再來看一個例子:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/about/')
def about():
# return render_template('about.html',user='zhiliao')
return render_template('about.html',**{
'user':'zhiliao'})
以上例子介紹了兩種傳遞參數的方式,因為render_template需要傳遞的是一個關鍵字參數,所以第一種方式是順其自然的。但是當你的模板中要傳遞的參數過多的時候,把所有參數放在一個函數中顯然不是一個好的選擇,因此我們使用字典進行包裝,並且加兩個*號,來轉換成關鍵字參數。
渲染模板例子:
視圖函數的主要作用是,處理業務邏輯,返回響應內容
flask是使用jinja2這個模板引擎來渲染模板
使用模板的好處
模板傳參
from flask import Flask,render_template
app = Flask(__name__)
student = {
'name': 'zhangsan',
'age':8,
'gender':'男'
}
student_list = [
{
'name': 'zhangsan','age':18,'gender':'男'},
{
'name': 'lisi','age':68,'gender':'女'},
{
'name': 'wangwu','age':16,'gender':'男'}
]
student_dict = {
'a':{
'name': 'zhangsan','age':18,'gender':'男'},
'b':{
'name': 'lisi','age':28,'gender':'女'},
'c':{
'name': 'wangwu','age':19,'gender':'男'}
}
@app.route('/test1')
def test1():
return render_template('01.html', **student) # 為了方便在模板中使用,可以把字典打散
@app.route('/test2')
def test2():
return render_template('02.html', stu_list = student_list)
@app.route('/test3')
def test3():
return render_template('03.html', stu_dict = student_dict)
if __name__ == '__main__':
app.run()
01.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第一個模板</title>
</head>
<body>
學生姓名:{
{
name }}
學生年齡:{
% if age >= 18 %}
已經成年
{
% else %}
未成年
{
% endif %}
學生性別:{
{
gender }}
</body>
</html>
結果
02.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第二個模板</title>
</head>
<body>
{
{
stu_list }}
<table border="1px">
<tr>
<td>序號</td>
<td>姓名</td>
<td>年齡</td>
<td>性別</td>
</tr>
{
% for stu in stu_list %}
<tr>
<td>{
{
loop.index }}</td>
<td>{
{
stu.name }}</td> <!-- 由於stu是字典,有三種寫法得到key的value-->
{
% if stu.age >= 60 %}
<td>已退休</td>
{
% elif stu.age >= 18 %}
<td>已成年</td>
{
% else %}
<td>未成年</td>
{
% endif %}
{
# <td>{
{ stu.get('age') }}</td>#}
<td>{
{
stu['gender'] }}</td>
</tr>
{
% endfor %}
</table>
</body>
</html>
結果
03.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第三個模板</title>
</head>
<body>
{
{
stu_dict }}
<table border="1px">
<tr>
<td>序號</td>
<td>姓名</td>
<td>年齡</td>
<td>性別</td>
</tr>
{
% for stu_key,stu in stu_dict.items() %}
<tr>
<td>{
{
loop.index }},key:{
{
stu_key }}</td>
<td>{
{
stu.name }}</td> <!-- 由於stu是字典,有三種寫法得到key的value-->
{
% if stu.age >= 60 %}
<td>已退休</td>
{
% elif stu.age >= 18 %}
<td>已成年</td>
{
% else %}
<td>未成年</td>
{
% endif %}
{
# <td>{
{ stu.get('age') }}</td>#}
<td>{
{
stu['gender'] }}</td>
</tr>
{
% endfor %}
</table>
</body>
</html>
結果
語法
更多參考原文章
過濾器是通過管道符號(|)進行使用的,例如:{ { name|length }},將返回name的長度。過濾器相當於是一個函數,把當前的變量傳入到過濾器中,然後過濾器根據自己的功能,再返回相應的值,之後再將結果渲染到頁面中。Jinja2中內置了許多過濾器,在這裡可以看到所有的過濾器,現對一些常用的過濾器進行講解:
abs(value):返回一個數值的絕對值。 例如:-1|abs。
default(value,default_value,boolean=false):如果當前變量沒有值,則會使用參數中的值來代替。name|default(‘xiaotuo’)——如果name不存在,則會使用xiaotuo來替代。boolean=False默認是在只有這個變量為undefined的時候才會使用default中的值,如果想使用python的形式判斷是否為false,則可以傳遞boolean=true。也可以使用or來替換。
escape(value)或e:轉義字符,會將<、>等符號轉義成HTML中的符號。例如:content|escape或content|e。
first(value):返回一個序列的第一個元素。names|first。
format(value,*arags,**kwargs):格式化字符串。例如以下代碼:
{
{
"%s" - "%s"|format('Hello?',"Foo!") }}
將輸出:Helloo? - Foo!
last(value):返回一個序列的最後一個元素。示例:names|last。
length(value):返回一個序列或者字典的長度。示例:names|length。
join(value,d=u’'):將一個序列用d這個參數的值拼接成字符串。
下邊做實驗
safe(value):如果開啟了全局轉義,那麼safe過濾器會將變量關掉轉義。示例:content_html|safe。
int(value):將值轉換為int類型。
float(value):將值轉換為float類型。
lower(value):將字符串轉換為小寫。
upper(value):將字符串轉換為小寫。
replace(value,old,new): 替換將old替換為new的字符串。
truncate(value,length=255,killwords=False):截取length長度的字符串。
striptags(value):刪除字符串中所有的HTML標簽,如果出現多個空格,將替換成一個空格。
trim:截取字符串前面和後面的空白字符。
string(value):將變量轉換成字符串。
wordcount(s):計算一個長字符串中單詞的個數。
也可以自定義過濾器,但是用的不多~
所有的控制語句都是放在{% … %}中,並且有一個語句{% endxxx %}來進行結束,Jinja中常用的控制語句有if/for…in…,現對他們進行講解:
<ul>
{
% for user in users %}
<li>{
{
user.username|e }}</li>
{
% else %}
<li><em>no users found</em></li>
{
% endfor %}
</ul>
並且Jinja中的for循環還包含以下變量,可以用來獲取當前的遍歷狀態:
測試器主要用來判斷一個值是否滿足某種類型,並且這種類型一般通過普通的if判斷是有很大的挑戰的。語法是:if…is…,先來簡單的看個例子:
{
% if variable is escaped%}
value of variable: {
{
escaped }}
{
% else %}
variable is not escaped
{
% endif %}
以上判斷variable這個變量是否已經被轉義了,Jinja中內置了許多的測試器,看以下列表:
一、宏:
模板中的宏跟python中的函數類似,可以傳遞參數,但是不能有返回值,可以將一些經常用到的代碼片段放到宏中,然後把一些不固定的值抽取出來當成一個變量,以下將用一個例子來進行解釋:
{
% macro input(name, value='', type='text') %}
<input type="{
{ type }}" name="{
{ name }}" value="{
{ value|e }}">
{
% endmacro %}
以上例子可以抽取出了一個input標簽,指定了一些默認參數。那麼我們以後創建input標簽的時候,可以通過他快速的創建:
<p>{
{
input('username') }}</p>
<p>{
{
input('password', type='password') }}</p>
二、import語句:
在真實的開發中,會將一些常用的宏單獨放在一個文件中,在需要使用的時候,再從這個文件中進行導入。import語句的用法跟python中的import類似,可以直接import…as…,也可以from…import…或者from…import…as…,假設現在有一個文件,叫做forms.html,裡面有兩個宏分別為input和textarea,如下:
{
% macro input(name, value='', type='text') %}
<input type="{
{ type }}" value="{
{ value|e }}" name="{
{ name }}">
{
% endmacro %}
{
% macro textarea(name, value='', rows=10, cols=40) %}
<textarea name="{
{ name }}" rows="{
{ rows }}" cols="{
{
cols
}}">{
{
value|e }}</textarea>
{
% endmacro %}
三、導入宏的例子:
{
% import 'forms.html' as forms %}
<dl>
<dt>Username</dt>
<dd>{
{
forms.input('username') }}</dd>
<dt>Password</dt>
<dd>{
{
forms.input('password', type='password') }}</dd>
</dl>
<p>{
{
forms.textarea('comment') }}</p>
{
% from 'forms.html' import input as input_field, textarea %}
<dl>
<dt>Username</dt>
<dd>{
{
input_field('username') }}</dd>
<dt>Password</dt>
<dd>{
{
input_field('password', type='password') }}</dd>
</dl>
<p>{
{
textarea('comment') }}</p>
另外需要注意的是,導入模板並不會把當前上下文中的變量添加到被導入的模板中,如果你想要導入一個需要訪問當前上下文變量的宏,有兩種可能的方法:
顯式地傳入請求或請求對象的屬性作為宏的參數。
與上下文一起(with context)導入宏。
與上下文中一起(with context)導入的方式如下:
{
% from '_helpers.html' import my_macro with context %}
include和set語句
一、include語句:
include語句可以把一個模板引入到另外一個模板中,類似於把一個模板的代碼copy到另外一個模板的指定位置,看以下例子:
{
% include 'header.html' %}
主體內容
{
% include 'footer.html' %}
<h3>網頁頭</h3>
<h3>網頁尾部</h3>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{
% include 'header.html' %}
<p>網頁內容</p>
{
% include 'footer.html'%}
</body>
</html>
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route("/test")
def test():
return render_template('test.html')
if __name__ == '__main__':
app.run(debug=True)
結果:
二、賦值(set)語句:
有時候我們想在在模板中添加變量,這時候賦值語句(set)就派上用場了,先看以下例子:
{
% set name='zhiliao' %}
那麼以後就可以使用name來代替zhiliao這個值了,同時,也可以給他賦值為列表和元組:
{
% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
賦值語句創建的變量在其之後都是有效的,如果不想讓一個變量污染全局環境,可以使用with語句來創建一個內部的作用域,將set語句放在其中,這樣創建的變量只在with代碼塊中才有效,看以下示例:
{
% with %}
{
% set foo = 42 %}
{
{
foo }} foo is 42 here
{
% endwith %}
也可以在with的後面直接添加變量,比如以上的寫法可以修改成這樣:
{
% with foo = 42 %}
{
{
foo }}
{
% endwith %}
這兩種方式都是等價的,一旦超出with代碼塊,就不能再使用foo這個變量了。
Flask中的模板可以繼承,通過繼承可以把模板中許多重復出現的元素抽取出來,放在父模板中,並且父模板通過定義block給子模板開一個口,子模板根據需要,再實現這個block,假設現在有一個base.html這個父模板,代碼如下:
以上父模板中,抽取了所有模板都需要用到的元素html、body等,並且對於一些所有模板都要用到的樣式文件style.css也進行了抽取,同時對於一些子模板需要重寫的地方,比如title、head、body都定義成了block,然後子模板可以根據自己的需要,再具體的實現。以下再來看子模板的代碼:
效果如下:
轉義的概念是,在模板渲染字符串的時候,字符串有可能包括一些非常危險的字符比如<、>等,這些字符會破壞掉原來HTML標簽的結構,更嚴重的可能會發生XSS跨域腳本攻擊,因此如果碰到<、>這些字符的時候,應該轉義成HTML能正確表示這些字符的寫法,比如>在HTML中應該用<來表示等。
但是Flask中默認沒有開啟全局自動轉義,針對那些以.html、.htm、.xml和.xhtml結尾的文件,如果采用render_template函數進行渲染的,則會開啟自動轉義。並且當用render_template_string函數的時候,會將所有的字符串進行轉義後再渲染。而對於Jinja2默認沒有開啟全局自動轉義,作者有自己的原因:
在沒有開啟自動轉義的模式下(比如以.conf結尾的文件),對於一些不信任的字符串,可以通過{ { content_html|e }}或者是{ { content_html|escape }}的方式進行轉義。在開啟了自動轉義的模式下,如果想關閉自動轉義,可以通過{ { content_html|safe }}的方式關閉自動轉義。而{%autoescape true/false%}…{%endautoescape%}可以將一段代碼塊放在中間,來關閉或開啟自動轉義,例如以下代碼關閉了自動轉義:
{
% autoescape false %}
<p>autoescaping is disabled here
<p>{
{
will_not_be_escaped }}
{
% endautoescape %}
一、數據類型:
Jinja支持許多數據類型,包括:字符串、整型、浮點型、列表、元組、字典、True/False。
二、運算符:
Web應用中會出現大量的靜態文件來使得網頁更加生動美觀。類似於CSS樣式文件、JavaScript腳本文件、圖片文件、字體文件等靜態資源。在Jinja中加載靜態文件非常簡單,只需要通過url_for全局函數就可以實現,看以下代碼:
<link href="{
{ url_for('static',filename='about.css') }}">
url_for函數默認會在項目根目錄下的static文件夾中尋找about.css文件,如果找到了,會生成一個相對於項目根目錄下的/static/about.css路徑。當然我們也可以把靜態文件不放在static文件夾中,此時就需要具體指定了,看以下代碼:
app = Flask(__name__,static_folder='C:\static')
那麼訪問靜態文件的時候,將會到/static這個文件夾下尋找。
之前我們接觸的視圖都是函數,所以一般簡稱視圖函數。其實視圖也可以基於類來實現,類視圖的好處是支持繼承,但是類視圖不能跟函數視圖一樣,寫完類視圖還需要通過app.add_url_rule(url_rule,view_func)來進行注冊。以下將對兩種類視圖進行講解:
一、標准類視圖:
標准類視圖是繼承自flask.views.View,並且在子類中必須實現dispatch_request方法,這個方法類似於視圖函數,也要返回一個基於Response或者其子類的對象。以下將用一個例子進行講解:
from flask.views import View
class PersonalView(View):
def dispatch_request(self):
return "知了課堂"
類視圖通過add_url_rule方法和url做映射
app.add_url_rule('/users/',view_func=PersonalView.as_view('personalview'))
二、基於調度方法的視圖:
Flask還為我們提供了另外一種類視圖flask.views.MethodView,對每個HTTP方法執行不同的函數(映射到對應方法的小寫的同名方法上),以下將用一個例子來進行講解:
class LoginView(views.MethodView):
# 當客戶端通過get方法進行訪問的時候執行的函數
def get(self):
return render_template("login.html")
# 當客戶端通過post方法進行訪問的時候執行的函數
def post(self):
email = request.form.get("email")
password = request.form.get("password")
if email == '[email protected]' and password == '111111':
return "登錄成功!"
else:
return "用戶名或密碼錯誤!"
# 通過add_url_rule添加類視圖和url的映射,並且在as_view方法中指定該url的名稱,方便url_for函數調用
app.add_url_rule('/myuser/',view_func=LoginView.as_view('loginview'))
如果用類視圖,我們怎麼使用裝飾器呢?比如有時候需要做權限驗證的時候,比如看以下例子:
from flask import session
def login_required(func):
def wrapper(*args,**kwargs):
if not session.get("user_id"):
return 'auth failure'
return func(*args,**kwargs)
return wrapper
裝飾器寫完後,可以在類視圖中定義一個屬性叫做decorators,然後存儲裝飾器。以後每次調用這個類視圖的時候,就會執行這個裝飾器。示例代碼如下:
class UserView(views.MethodView):
decorators = [user_required]
...
一、藍圖
之前我們寫的url和視圖函數都是處在同一個文件,如果項目比較大的話,這顯然不是一個合理的結構,而藍圖可以優雅的幫我們實現這種需求。以下看一個使用藍圖的文件的例子:
靜態文件默認到static文件夾中查找,模板文件默認到templates文件夾下查找,一般不建議修改默認路徑~
二、子域名
子域名在許多網站中都用到了,比如一個網站叫做xxx.com,那麼我們可以定義一個子域名cms.xxx.com來作為cms管理系統的網址,子域名的實現一般也是通過藍圖來實現,在之前章節中,我們創建藍圖的時候添加了一個url_prefix=/user作為url前綴,那樣我們就可以通過/user/來訪問user下的url。但使用子域名則不需要。另外,還需要配置SERVER_NAME,比如app.config[SERVER_NAME]=‘example.com:9000’。並且在注冊藍圖的時候,還需要添加一個subdomain的參數,這個參數就是子域名的名稱,先來看一下藍圖的實現(admin.py):
from flask import Blueprint
bp = Blueprint('admin',__name__,subdomain='admin')
@bp.route('/')
def admin():
return 'Admin Page'
這個沒有多大區別,接下來看主app的實現:
from flask import Flask
import admin
# 配置`SERVER_NAME`
app.config['SERVER_NAME'] = 'example.com:8000'
# 注冊藍圖,指定了subdomain
app.register_blueprint(admin.bp)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8000,debug=True)
寫完以上兩個文件後,還是不能正常的訪問admin.example.com:8000這個子域名,因為我們沒有在host文件中添加域名解析,你可以在最後添加一行127.0.0.1 admin.example.com,就可以訪問到了。另外,子域名不能在127.0.0.1上出現,也不能在localhost上出現。
MySQL數據庫:
在網站開發中,數據庫是網站的重要組成部分。只有提供數據庫,數據才能夠動態的展示,而不是在網頁中顯示一個靜態的頁面。數據庫有很多,比如有SQL Server、Oracle、PostgreSQL以及MySQL等等。MySQL由於價格實惠、簡單易用、不受平台限制、靈活度高等特性,目前已經取得了絕大多數的市場份額。因此我們在Flask中,也是使用MySQL來作為數據存儲。
1、在MySQL的官網下載MySQL數據庫安裝文件:https://dev.mysql.com/downloads/mysql/
2、安裝步驟略
安裝完MySQL數據庫以後,就可以使用MySQL提供的終端客戶端軟件來操作數據庫。如下:
這個軟件所有的操作都是基於sql語言,對於想要熟練sql語言的同學來講是非常合適的。但是對於在企業中可能不是一款好用的工具。在企業中我們推薦使用mysql workbench以及navicat這種圖形化操作的軟件。而mysql workbench是mysql官方提供的一個免費的軟件,正因為是免費,所以在一些功能上不及navicat。navicat for mysql是一款收費的軟件。官網地址如下:https://www.navicat.com.cn/products。使用的截圖如下:
我們使用Django來操作MySQL,實際上底層還是通過Python來操作的。因此我們想要用Flask來操作MySQL,首先還是需要安裝一個驅動程序。在Python3中,驅動程序有多種選擇。比如有pymysql以及mysqlclient等。這裡我們就使用mysqlclient來操作。mysqlclient安裝非常簡單。只需要通過pip install mysqlclient即可安裝。
1、SQLAlchemy和Flask-SQLAlchemy的區別:
SQLAlchemy:是一個獨立的ORM框架,可以獨立於Flask存在,也可以在其他項目中使用,比如在Django中。
Flask-SQLAlchemy:對SQLAlchemy的一個封裝,能夠更適合在flask中使用。(所以此處沒有必要從SQLAlchemy學起)
2、安裝和驗證:
3、連接數據庫:
# 數據庫的配置變量
HOSTNAME = '127.0.0.1' #默認
PORT = '3306' #默認
DATABASE = 'xt_flask' #數據庫名按實際情況填寫
USERNAME = 'root' #按實際情況填寫
PASSWORD = 'root' #按實際情況填寫
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
@app.route('/')
def hello_world():
# 寫一個測試代碼來驗證是否連接成功
engine = db.get_engine()
with engine.connect() as conn:
result = conn.execute("select 1")
print(result.fetchone())
return 'Hello World!'
if __name__ == '__main__':
app.run()