一個項目的簡單結構劃分
首先創建一個新項目
可以正常運行與訪問
創建配置文件並添加配置。
將這裏拆分到不同的文件中,讓啓動文件更加簡潔。
創建一個apps包,導入配置模塊,導入Flask,定義創建app函數,返回app對象。實例化的Flask對象做配置,定義模板位置和靜態文件位置。添加app config配置,使用來自對象的方法,將配置模塊導入到配置中
然後再來寫我們的運行程序。導入創建 app函數,創建app,讓app點run運行
我們將路由和視圖函數寫到views裏面,這樣啓動文件就簡潔了。我們將配置文件寫到一個模塊裏面,這樣創建好Flask對象之後,不用app.config往後堆很多配置,簡潔很多。使用app點config點來自對象,將模塊作爲參數傳入進去。
一個項目:比如分用戶 商品 訂單
一個項目分很多部分。我們不可能把所有的路由放到一個文件。
比如用戶,就可以有
用戶中心 /center
用戶註冊 /register
用戶登錄 /login
用戶更新 /upgrade
一個用戶部分就有很多路由。那麼我們可以項目的一個部分就用一個藍圖,把路由劃分成不同的部分。其實藍圖也是路由的另一種方式。
我們在apps下創建視圖模塊,裏面寫一個藍圖。藍圖裏寫了兩個視圖函數,
user_for('')調用register,如果有endpoint,返回的是endpoint的值,如果沒有,返回的是函數的名,這是反向解析。就是給我個名我去找到路徑,而不是給個路徑我去找到函數名。正常的是給個路由,我給你找到函數名,現在反過來了,你給我函數名我給你找到路由,這就是url_for反向解析,
寫完藍圖後導入並註冊到app中
註冊完了之後就命令行運行app程序並訪問
一訪問就報錯了
因爲原來我們是在app上寫路由,現在我們用了藍圖了,多了一層,經過藍圖然後寫的路由。所以url_for想要反向解析引用函數名,就需要在前面加上藍圖,指定是哪個藍圖下的函數名,然後我們再訪問
這樣就訪問到了默認路由根了。
後端打印出 我們根據字符串反向解析出路徑。包括在前端進行反向解析的時候,也要這麼寫,加上藍圖名稱
一個項目
我們這裏沿用上面的結構做些修改。
我們將路由和視圖函數寫到views裏面,這樣啓動文件就簡潔了。我們將配置文件寫到一個模塊裏面,這樣創建好Flask對象之後,不用app.config往後堆很多配置,簡潔很多。使用app點config點來自對象,將模塊作爲參數傳入進去。
項目:比如分用戶 商品 訂單
一個項目分很多部分。我們不可能把所有的路由放到一個文件。
比如用戶,就可以有
用戶中心 /center
用戶註冊 /register
用戶登錄 /login
用戶更新 /upgrade
一個用戶部分就有很多路由。那麼我們可以項目的一個部分就用一個藍圖,把路由劃分成不同的部分。其實藍圖也是路由的另一種方式。
我們可以在apps下創建多個目錄,用戶目錄放用戶相關的,商品目錄放商品相關,放的可以是藍圖等,比如view模塊裏面定義藍圖。
現在目錄結構如下:
我們根據項目的不同的部分劃分。這裏創建了三個包,我們現在要寫跟用戶相關的,所以我們寫藍圖在user下面的view模塊寫藍圖。app,配置還有init文件已經按上面那個寫好了。現在只需要把藍圖重新寫一下。
程序:
from flask import Flask import settings from apps.user.view import user_bp def create_app(): app=Flask(__name__,template_folder='../templates',static_folder='../static') app.config.from_object(settings) app.register_blueprint(user_bp) print(app.url_map) return app
from flask import Blueprint user_bp=Blueprint('user',__name__) @user_bp.route("/") def user_center(): return '用戶中心' @user_bp.route('/register',methods=['GET','POST']) def register(): return '用戶註冊' @user_bp.route('/login',methods=['GET','POST']) def login(): return '用戶登錄' @user_bp.route('/logout',methods=['GET','POST']) def logout(): return '用戶註冊'
用戶下寫的藍圖:
現在我們寫了四個路由了。這四個路由綁定在藍圖上的,也就是綁定在user藍圖上的。藍圖需要註冊到app中,讓藍圖和app產生聯繫。
在apps下的init裏面,導入藍圖,在app對象裏面註冊我們創建的藍圖,我們可以用url_map查看app裏的路由,可以看到我們寫的四個路由,已經生效了。
我們訪問一下看效果:
我們需要定義類,用於與數據庫交互用的。在用戶目錄下創建model模塊。寫入到這個模塊中
現在我們需要寫模板了,模板目錄創建子目錄user,跟用戶相關的模板就放到這個目錄中。比如我們先創建三個文件,登錄,註冊和展示用的頁面。因爲這些頁面都有公共的部分。所以我們需要將公共的部分分出來。我們在templates下創建base頁面。
母版的寫法:
base母版頁面的標題也是可能改的,設置成block,默認是用戶中心
我們還需要預留我們的css和js。所以母版中再設置css和js塊。然後我們創建三個div,分爲header center footer.然後定義三者的樣式
剛剛的有點問題,右擊模板目錄,將目錄標記爲模板目錄,使用jinja2語言。這樣下面的block就識別出來了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} 用戶中心{% endblock %}</title> <style> #head { height: 50px; background-color: bisque; } #head ul { list-style: none; height: 50px; } #head ul li { float: left; width: 100px; text-align: center; font-size: 18px; height: 50px; line-height: 50px; } #middle { height: 900px; background-color: azure; } #foot { height: 50px; line-height: 50px; background-color: darkseagreen; } </style> {% block mycss %}{% endblock %} </head> <body> <div id="head"> <ul> <li><a href="">首頁</a></li> <li><a href="">秒殺</a></li> <li><a href="">超市</a></li> <li><a href="">圖書</a></li> <li><a href="">會員</a></li> </ul> </div> <div id="middle"> {% block middle %} {% endblock %} </div> <div id="foot"> </div> {% block myjs %}{% endblock %} </body> </html>
母版展示效果如下
我們要寫註冊頁面。我們需要繼承母版。修改標題,
{% extends 'base.html' %} {% block title %} 用戶註冊 {% endblock %} {% block middle %} <form action="/register" method="post"> <p><input type="text" name="username" placeholder="用戶名"></p> <p><input type="password" name="password" placeholder="密碼"></p> <p><input type="password" name="repassword" placeholder="確認密碼"></p> <p><input type="number" name="phone" placeholder="手機號碼"></p> <p><input type="submit" value="用戶註冊"></p> </form> {% endblock %}
將
我們寫好前端頁面之後。寫後端,讓註冊視圖返回這個模板文件看看效果。因爲模板目錄能找到,文件在子目錄中,所以用相對路徑就可以找到文件。
效果如下:
我們在創建flask對象的時候,設置了模板文件位置,靜態文件位置了。所以flask能找到模板目錄。如果在創建flask對象的時候沒有設置傳參那麼就只能把模板目錄放到apps下,和init文件同級目錄,因爲,創建flask對象的時候,內部有默認設置找到創建對象同級目錄下的templates目錄作爲模板目錄。同理,我們也要設置一下靜態文件目錄、。因爲我們要將apps和模板目錄獨立開來,不放到apps下。
接下來我們寫post提交數據。點擊註冊,然後跳轉到用戶中頁面
目前我們的model這個類,可以封裝用戶的用戶名,密碼。打印對象會顯示用戶名。用戶數據封裝在init方法下面。
用戶數據暫時先保存到一個列表中。有一個就追加一個到列表中。將用戶類導入進來,創建用戶對象,將用戶數據封裝進對象中。註冊後是將用戶對象追加到用戶對象列表裏面了。
然後看一下用戶註冊的邏輯。
- 如果是get請求,那麼不走post下面的程序,直接走到最後的一個return,返回一個註冊頁面可以讓瀏覽器展示出來。
- 如果我們在from的框框裏輸入數據,點擊用戶註冊,那麼就是發送post請求,走post下面的程序。
- 這時,第一步就是獲取post提交的數據。獲取form數據,是request點form裏去get數據。form返回的是個啥字典的形式數據集,雖然展示不是字典的樣子,但是用get可以獲取到指定鍵的值。
- 對密碼和確認密碼做判斷,兩次密碼相等才能繼續往下走,然後循環用戶對象列表,查詢前端提交的用戶名字是否存在用戶對象列表中中。如果存在就返回註冊頁面和一個錯誤信息,錯誤信息顯示是用戶名已存在。並將信息渲染在模板中。否則,就是用戶不存在,繼續往下走,用戶不存在就可以創建用戶對象了。將前端form數據封裝到用戶對象裏,然後將用戶對象追加到用戶對象列表存儲起來。這就相當於保存到數據庫了。
- 保存數據之後就跳轉到用戶展示頁面。這裏是用戶中心,也就是"/"
查看效果
給我們的註冊頁面添加上錯誤顯示
<p style="color: red">{{ msg }}</p>
目前缺少很多校驗
成功跳轉到用戶中心頁面
接下來寫用戶中心的頁面,用戶中心頁面是展示用的,那麼我們也就返回一個展示模板,因爲需要展示用戶的,那就將users對象列表傳進去。傳的方法是變量等於用戶對象列表。
用戶中心展示頁面,我們得知道需要展示哪些新喜,標題,當前用戶人數等。然後每個人的信息。這裏就做個表格,將後端傳進來的用戶對象列表做循環。然後點取值
如下,我們進入註冊頁面,註冊後跳轉到用戶信息頁面。這樣就將信息都展示出來了。用戶信息需要修改和刪除按鈕、。loop.index應該是循環的索引
我們應當還需要添加。一般數據展示頁面,需要增改刪按鈕,查已經實現了、。
現在我們需要刪除
刪除一條數據,一個用戶。我們需要準確的知道是刪除那條數據。點擊刪除按鈕或者是鏈接,就發送請求,讓它請求/del刪除路徑。這樣讓它找到刪除用戶操作的刪除視圖函數。這個功能的實現需要js來配合實現。前端上是添加一個js點擊事件,
下面看下如何添加js點擊事件。
在刪除a標籤上添加點擊事件函數傳一個字符串用戶的名字。這裏是根據用戶名去刪除。下面就添加js代碼塊。定義js語句。定義js函數,函數是點擊事件中調用的函數。function 函數名小括號接收點擊事件傳給函數的參數,然後花括號定義點擊事件發生後的行爲。這裏暫時是打印在控制檯。js函數的調用時,給href鏈接加上Javascript冒號分號,然後後面接事件類型,事件是點擊事件=雙引號,雙引號裏面可以放函數的調用。調用的括號裏面可以放給函數的傳參。傳參這裏是傳的字符串。傳的是一個用戶名字這個變量。這樣,點擊誰,就傳參是誰,下面的函數可以定位到對那條數據做刪除操作。
點擊之後成功打印,說明沒有問題。後面再改成刪除的行動
刪除的邏輯應該是如下:
現在當我們點擊刪除a鏈接時,不是打印輸出在控制檯了。是要發送一個請求。我們這裏註釋掉a標籤的請求,使用調用js函數的方式來做請求。做的是個get請求。也就是刪除按鈕是個點擊事件。當點擊這個標籤時,調用del函數並傳參用戶名字給函數。js代碼塊中定義的del函數接收到傳參,知道是哪個用戶要被刪除,函數要做的動作是想/del路徑發送get請求,並拼接上用戶名,因爲這裏是根據用戶名來刪除,將用戶名作爲get請求的參數傳遞給後端視圖函數。後端視圖函數接收到傳參後就可以對該數據做操作了。js中location.href等於一個地址,可以使得函數執行時,對該地址發送一個get請求。拼接鍵值對用問號,鍵自己指定,值是js函數傳參接收過來的用戶名。鍵值對等號拼接
刪除邏輯如下:前端想del發送一個請求,並將用戶傳到後端了,視圖函數中獲取get請求中的數據,然後對用戶做判斷。循環我的用戶列表.如果用戶對象列表的每個用戶名字和前端傳遞過了的名字相等,那麼就將用戶從用戶對象列表中刪除,然後重定向到用戶中心頁面。如果循環結束之後發現沒有匹配到用戶,也就沒走到重定向這一步,我們可以else否則給它返回一個刪除失敗。
這下我們點擊刪除
發現刪除成功
註冊模板中我們寫action地址,我們要訪問的是藍圖user下的/register這個路由,但是這個路由也是可能會被修改的。如果被修改了,前端action地址就訪問不到了,所以前端不能寫死了,我們應該用動態的反向解析的方法去獲取到路由地址,從而正確訪問到該路由。{{ url_for('user.register') }} 就是通過字符串,能反向解析出路由地址,然後訪問路由從而實現向這條路由發起請求。正常來說我們是通過路徑找到函數,而我們這裏就是通過函數名或endpoint去找到路徑,這就是反向解析
接下來我們要做修改的程序
前端設計:
- 用戶點擊修改,進入修改頁面。也就是在用戶展示頁面可以個修改a標籤加個點擊事件,調用修改函數。修改函數的動作就是給修改路徑/update發送一個請求,並將用戶名字傳遞給後端,這裏是發送一個get請求 ,get請求修改頁面,然後展示修改頁面。
- 修改頁面要繼承母版,修改標題,修改中間的div信息。修改的表單action,是去請求更新的路徑,我們後端寫一個路由和視圖函數,作爲修改使用。
- form的action,最好寫成反向解析,用變量引用的方式,url_for ,傳參藍圖點endpoint或者函數名,讓它找到訪問路徑,併發起請求
- 我們這裏的有三個字段,那修改也要有三個字段。這裏是通過用戶名來識別用戶的,沒有設置用戶id,所以修改提交操作後我們不知用戶改的是誰,這種情況下我們可以在修改頁面添加一個隱藏標籤,備註上它的名字。然後寫上用戶,密碼手機號三個標籤,更新按鈕。因爲是修改,當我們在用戶展示頁點擊修改後,向修改頁面發送get請求,讓後端將修改頁面和修改用戶的數據都傳遞過來。然後我們可以在模板的input框中設置默認值,value.
- 當點擊修改的時候,這裏是通過js從用戶展示頁向修改頁面/update發送一個get請求,後端/update的get請求我們可以返回修改頁面的模板,因爲是修改,所以修改的框中歐應該攜帶修改用戶的原本數據纔對。我們剛剛從用戶展示頁發送請求的時候,將點擊的用戶的名字通過參數的方式傳給點擊事件函數,函數接收參數,發送get請求拼接攜帶上用戶的名字。後端get請求邏輯代碼中,從get請求裏獲取到用戶名字,通過用戶名字遍歷用戶對象列表,如果有名字相等的用戶對象,就將該用戶傳到修改頁面模板中,也就是返回用戶模板和用戶對象。而模板中有定義使用用戶對象去渲染出修改前的原數據信息的。
下面看下展示效果
接下來就是寫/update路由也就是修改頁面的post請求了。
修改邏輯
- 修改頁面當修改完數據需要向修改頁面的路由/update發送一個post請求,我們這裏的路徑是用url_for反向解析,使用藍圖點endpoint去解析出請求路徑,然後向路徑發起post請求。
- 前端向/update發起post請求後,後端視圖函數走post請求的程序。post請求中有用戶提交的數據,在請求點form中,get通過前端name獲取對應填寫的值。然後遍歷用戶對象列表,如果用戶對象列表中有用戶的名字等於隱藏標籤的真實名字,也就是未修改前的名字,那麼就讓用戶對象的名字等於新提交的名字,也就是重新賦值,然後讓用戶對象的其它屬性也等於用戶新提交的數據,然後給前端展示返回一個更新成功。我們也可以返回一個重定向,重定向到用戶展示頁面
重定向到用戶展示頁面效果
目前的程序day042 05:
class User: def __init__(self, username, password, phone=None): self.username = username self.password = password self.phone = phone def __str__(self): return self.username
from flask import Blueprint,request, render_template, redirect from apps.user.model import User user_bp=Blueprint('user',__name__) # 列表保存的是一個一個的用戶對象 users = [] @user_bp.route("/") def user_center(): return render_template('user/show.html',users=users) @user_bp.route('/del') def del_user(): # 獲取你傳遞的username username = request.args.get('username') # 根據username找到列表中的user對象 for user in users: if user.username == username: # 刪除user users.remove(user) return redirect('/') else: return '刪除失敗' @user_bp.route('/update', methods=['POST', 'GET'], endpoint='update') def update_user(): if request.method == 'POST': realname = request.form.get('realname') username = request.form.get('username') password = request.form.get('password') phone = request.form.get('phone') for user in users: if user.username == realname: user.username = username user.phone = phone return redirect('/') else: # get請求了 username = request.args.get('username') for user in users: if user.username == username: return render_template('user/update.html', user=user) @user_bp.route('/register',methods=['GET','POST']) def register(): if request.method=='POST': # 獲取post提交的數據 print(request.form) print(request.form.get('username')) username = request.form.get('username') password = request.form.get('password') phone = request.form.get('phone') repassword = request.form.get('repassword') if password == repassword: # 用戶名唯一 for user in users: if user.username == username: return render_template('user/register.html', msg='用戶名已存在') # 創建user對象 user = User(username, password, phone) # 添加到用戶列表 users.append(user) return redirect('/') return render_template('user/register.html') @user_bp.route('/login',methods=['GET','POST']) def login(): return '用戶登錄' @user_bp.route('/logout',methods=['GET','POST']) def logout(): return '用戶註冊'
from flask import Flask import settings from apps.user.view import user_bp def create_app(): app=Flask(__name__,template_folder='../templates',static_folder='../static') app.config.from_object(settings) app.register_blueprint(user_bp) return app
{% extends 'base.html' %} {% block title %} 用戶註冊 {% endblock %} {% block middle %} <p style="color: red">{{ msg }}</p> <form action="{{ url_for('user.register') }}" method="post"> <p><input type="text" name="username" placeholder="用戶名"></p> <p><input type="password" name="password" placeholder="密碼"></p> <p><input type="password" name="repassword" placeholder="確認密碼"></p> <p><input type="number" name="phone" placeholder="手機號碼"></p> <p><input type="submit" value="用戶註冊"></p> </form> {% endblock %}
{% extends 'base.html' %} {% block middle %} <h1>用戶信息</h1> <span>當前用戶人數是:{{ users |length }} 人</span> <table border="1" cellspacing="0" width="60%"> {% for user in users %} <tr> <td>{{ loop.index }}</td> <td>{{ user.username }}</td> <td>{{ user.password }}</td> <td>{{ user.phone }}</td> <td><a href="javascript:;" onclick="update('{{ user.username }}')" >修改</a> <a href="javascript:;" onclick="del('{{ user.username }}')">刪除</a></td> </tr> {% endfor %} </table> {% endblock %} {% block myjs %} <script> function del(username) { {#console.log(username)#} {#// location 地址欄對象#} location.href = '/del?username=' + username } //修改的函數 function update(username) { location.href = '/update?username=' + username } </script> {% endblock %}
{% extends 'base.html' %} {% block title %} 用戶信息修改 {% endblock %} {% block middle %} <h1>用戶信息更新</h1> <form action="{{ url_for('user.update') }}" method="post"> <p><input type="hidden" name="realname" value="{{ user.username }}"></p> <p><input type="text" name="username" placeholder="用戶名" value="{{ user.username }}"></p> <p><input type="password" name="password" placeholder="密碼" value="{{ user.password }}" disabled></p> <p><input type="number" name="phone" placeholder="手機號碼" value="{{ user.phone }}"></p> <p><input type="submit" value="用戶更新"></p> </form> {% endblock %}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} 用戶中心{% endblock %}</title> <style> #head { height: 50px; background-color: bisque; } #head ul { list-style: none; height: 50px; } #head ul li { float: left; width: 100px; text-align: center; font-size: 18px; height: 50px; line-height: 50px; } #middle { height: 900px; background-color: azure; } #foot { height: 50px; line-height: 50px; background-color: darkseagreen; } </style> {% block mycss %}{% endblock %} </head> <body> <div id="head"> <ul> <li><a href="">首頁</a></li> <li><a href="">秒殺</a></li> <li><a href="">超市</a></li> <li><a href="">圖書</a></li> <li><a href="">會員</a></li> </ul> </div> <div id="middle"> {% block middle %} {% endblock %} </div> <div id="foot"> </div> {% block myjs %}{% endblock %} </body> </html>
from apps import create_app app=create_app() if __name__ == '__main__': app.run()
ENV="development" DEBUG=True