Posts

Showing posts with the label flask

shop_platform - 上傳圖片

Image
賣家上架商品時,可以展示商品圖片。表單原本只能填寫圖片網址。 現在要改成可以直接上傳圖片。 form 表單可以上傳圖片 前端 form 標籤屬性加上 enctype="multipart/form-data" 若沒有加的話,enctype 預設使用 application/x-www-form-urlencoded,這樣的話: 後端只收得到檔案名稱: request.form.get('field_name') 是檔案名稱 收不到檔案內容: request.files.get('field_name') 是 None input 標籤,改成 type="file" ,前端防呆加上 accept=".jpg,.jpge,.png,.gif" 後端 驗證檔案副檔名 驗證檔案大小 將圖片存在伺服器 後端收到的檔案,資料型態是 werkzeug.datastructures.FileStorage ,可以用 save 方法,把檔案存到指定檔案,目前是存在 static/product_images/ 資料夾。 為了避免不同使用者,不約而同上傳同樣名稱的檔案,我用 uuid 作為新的檔案名稱,再加上副檔名。 前端會用 /static/product_images/<string:filename> 這個網址請求圖片資源。 目前圖片的網址,透露了後端是用什麼資料夾結構儲存圖片資源。但路由和真正的檔案位置其實是可以分開的,設定好路由,在路由中帶入檔名,然後用 flask.send_from_directory 就可以指定從哪個資料夾取出檔案,這樣一來,網址就不會暴露檔案位置了。 commit: feat: user can upload product picture 將圖片存在 imgur 伺服器目前部署在 heroku 上。因為 heroku 的免費方案,有 the maximum slug size is 500 MB 的限制,所以要把圖片都存在伺服器有困難。因此想改成存在 imgur 上。 imgur api 官網 推薦...

shop_platform - 部署到 Heroku 後,session 有問題

問題描述 部署到 heroku 後,出現一些奇怪的現象: 輸入正確登入資訊,按下登入按鈕後: 有時候會登入成功,跳轉到首頁,但切換幾個畫面後,又變成沒有登入的狀態 跳轉到首頁,但仍是未登入狀態,也沒有出現任何 flash 訊息。切換幾個畫面後,出現遲來的 flash 訊息 仍是跳轉到登入頁面,沒有出現任何 flash 訊息 在未登入狀態下,將物品加入購物車(此時購物車內容會存在 session)。點選購物車頁面後: 有時候會跳轉到首頁,也沒有出現任何 flash 訊息。切換幾個畫面後,出現遲來的 flash 訊息 成功顯示購物車中的商品,但若繼續點選購物車頁面,過沒多久就提示「購物車中沒有商品」 上述操作(導覽列判斷使用者是否已登入、flash、未登入時的購物車),多半是跟 session 有關係,讓我有一種「cookie 或 session 好像會突然出現,又突然消失」的感覺。 另外,如果在本地也跟 heroku 一樣使用 gunicorn "app:create_app()" 指令開啟伺服器,一切功能都正常,無法重現 heroku 上的問題。 查資料 Flask sessions not persisting on heroku - stackoverflow Application Server、WSGI Server、Web Server 之間的關係 nginx+gunicorn/uwsgi+python web 的前世今生 Flask想上線? 你還需要一些酷東西 Heroku gunicorn flask login is not working properly - stackoverflow gunicorn 19 with WebSocket on Heroku These applications based on the Flask-SocketIO extension are stateful. If multiple workers ar...

shop_platform - sqlalchemy.exc.TimeoutError

發生的問題 啟動伺服器後,第二十一次請求「需要查詢資料庫」的頁面時,會出現「sqlalchemy.exc.TimeoutError: QueuePool limit of size 10 overflow 10 reached, connection timed out, timeout 30.00 (Background on this error at: https://sqlalche.me/e/14/3o7r )」的錯誤訊息。如果請求是不需要查詢資料庫的(例如靜態檔案或前往登入頁面),就不會發生此錯誤。 把 size 和 overflow 設定成更小的數字,可以更快重現這個錯誤 。 感覺起來,就像查詢完資料庫以後,沒有把 connection 釋出一樣,導致「size + overflow」是多少,就只能查詢多少次資料庫。 發現瀏覽器每向伺服器請求一次「需要查詢資料庫」的頁面,在 MySQL Workbench 用 show PROCESSLIST; 指令的查詢結果就會多出一筆資料,筆數達到 size + overflow 時,若再次請求「需要查詢資料庫」的頁面,就會噴 QueuePool limit of size 10 overflow 10 reached 的錯誤。關閉伺服器時,那幾筆資料才會被刪除。 嘗試過但失敗的方法 app.run(debug=False) 自己註冊一個關閉 session 的函式 @app.teardown_appcontext # 改成 @app.teardown_request 也沒用 def teardown_db(exception): db.session.remove() # 改成 db.session.close() 也沒用 print('***** session.remove') # 這一行有印出來,所以此函式有被執行 把 FLASK_ENV=development 拿掉 ,於是 FLASK_ENV 變成預設的 production app.config["SQLALCHEMY_COMMIT_ON_TEARDOWN"] = True 終於成功的方法 第一種方法:用 Ap...

shop_platform - 新增種子資料時報錯

想完成的事 建立 user 的種子資料 遇到的問題,和解決過程 直接在 MySQL workbench 新增資料沒有問題,但是用 flask_seeder 新增資料會報錯。縮小範圍後,發現使用 flask_sqlalchemy 新增資料就會報錯。 錯誤訊息如下: Traceback (most recent call last): File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py", line 2744, in _determine_joins self.primaryjoin = join_condition( File "<string>", line 2, in join_condition File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/selectable.py", line 1184, in _join_condition cls._joincond_trim_constraints( File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/selectable.py", line 1305, in _joincond_trim_constraints raise exc.AmbiguousForeignKeysError( sqlalchemy.exc.AmbiguousForeignKeysError: Can't determine join between 'user' and 'order'; tables have more tha...

shop_platform - Composite Foreign Keys

想要達到的效果: 現有 order_item 這張表。現在要建立 rating table ,並且建立 order_item 和 rating 之間的 one-one relationship。但 order_item 的 primary key 是 composite primary key,同時有兩個 column 都是 primary key。不知道要怎麼在 rating 建立外鍵。 order_item 的程式碼: from app import db from datetime import datetime class OrderItem(db.Model): __tablename__ = 'order_item' order_id = db.Column(db.Integer, db.ForeignKey('order.id'), primary_key=True) product_id = db.Column(db.Integer, db.ForeignKey('product.id'), primary_key=True) price = db.Column(db.Integer, nullable=False) quantity = db.Column(db.Integer, nullable=False) insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now) update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now) order = db.relationship('Order', back_populates='products') product = db.relationship('Product', back_populates='orders') 參考資料 用「sqlalchemy foreign...

shop_platform - flask db downgrade 遇到 TypeError

事發經過 發現建立 cart table 時,漏掉 user_id 這個 foreign key,所以幫 user 和 cart 兩張資料表補上 one-one relationship。 class User(db.Model): __tablename__ = 'user' id = db.Column(db.Integer, primary_key=True) display_name = db.Column(db.String(50), unique=True, nullable=False) email = db.Column(db.String(50), unique=True, nullable=False) password = db.Column(db.String(140), nullable=False) cell_phone = db.Column(db.String(20)) address = db.Column(db.String(100)) store_introduction = db.Column(db.String(2000)) role = db.Column(db.String(10), nullable=False, default='user') insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now) update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now) cart = db.relationship('Cart', back_populates='user', uselist=False) delivery_orders = db.relationship('Order', back_populates='seller') purchase_order = db.relati...

shop_platform - 建立多對多關聯:Association Object

根據 SQLAlchemy 的 Basic Relationship Patterns ,建立多對多關聯有兩種方式: join table 上只有外鍵欄位:使用 association table join table 上除了外鍵,還有其他欄位:使用 association object association object 不同欄位間的關係: class Association (Base):     __tablename__ = 'association'     left_id = Column(ForeignKey(' left.id '), primary_key=True)     right_id = Column(ForeignKey(' right.id '), primary_key=True)     extra_data = Column(String(50))     child = relationship(" Child ", back_populates=" parents ")     parent = relationship(" Parent ", back_populates=" children ") class Parent (Base):     __tablename__ = ' left '     id = Column(Integer, primary_key=True)     children = relationship(" Association ", back_populates=" parent ") class Child (Base):     __tablename__ = ' right '     id = Column(Integer, primary_key=True)     parents = relationship(" Association ", back_populates...

shop_platform - flask db init fails: KeyError 'migrate'

試著使用 Flask-Migrate 做 MySQL 的 migration。但是在終端機使用 flask db init 指令時失敗了。 程式碼: common/database.py: from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate db = SQLAlchemy() migrate = Migrate() app.py: import os from flask import Flask from dotenv import load_dotenv from common.database import db, migrate if os.environ.get('FLASK_ENV', '') != 'production': load_dotenv() app = Flask(__name__) app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('SQLALCHEMY_DATABASE_URI') @app.route('/') def home(): return 'Hello World!' if __name__ == '__main__': db.init_app(app) migrate.init_app(app, db) app.run() 錯誤訊息: File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/bin/flask", line 8, in <module> sys.exit(main()) File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR...

shop_platform - 規格

因為有認識的朋友想在網路上賣東西,想請我幫他寫個網站,所以試著做做看。 user story 管理員 可以修改分類 可以刪除留言 已登入的使用者 身為賣家 ✓ 可以上架商品 ✓ 可以依分類瀏覽所有上架商品 可以回覆買家的提問 訂單成立時,可以收到通知信 ✓ 可以瀏覽所有售貨訂單 可以修改訂單出貨狀態 可以取消訂單 對每筆訂單,可以評價此次售貨體驗 身為買家 ✓ 可以使用訪客的所有功能 可以向賣家提問 ✓ 可以結帳 ✓ 結帳後,會收到通知信 ✓ 可以線上刷卡付款 ✓ 可以瀏覽所有購物訂單 ✓ 可以取消尚未付款且尚未出貨的訂單 對每筆訂單,可以評價此次購物體驗 訪客 ✓ 可以依分類瀏覽商品 ✓ 可以搜尋商品 可以瀏覽商品得到的評價 可以瀏覽賣家得到的評價 ✓ 可以將商品放入購物車 ✓ 可以瀏覽購物車 ✓ 可以調整購物車中的商品數量 GitHub 最後更新日期:2021 年 10 月 15 日

flask 小抄(Pipenv 版)(Flask 2.0.1)

初始化專案 建立並移動到專案資料夾 用 pipenv 將專案初始化: pipenv --python 3.9 安裝 Flask 套件: pipenv install flask 在根目錄建立 app.py 設定環境變數: export FLASK_APP=app.py export FLASK_ENV=development (存擋後會自動重啟 app) .gitignore: .gitignore 產生器 Application Factories  起手式: # app.py from flask import Flask, render_template, request def create_app():     app = Flask(__name__)     # do something     return app 參考資料 为Git仓库里的.idea文件夹正名 執行專案 flask run pipenv or pipenv run python app.py 在瀏覽器網址列輸入 http://127.0.0.1:5000/ Jinja2 Builtin Filter 連接資料庫 mongodb 安裝套件(先確定有啟動 venv): pipenv install pymongo or support for mongodb+srv:// URIs requires dnspython: pipenv install pymongo[srv] PyMongo 3.12.0 Documentation 連線設定: from pymongo import MongoClient client = MongoClient('url') app.db = client['databas...

flask 小抄(pip + virtualenv 版)(Flask 2.0.1)

初始化專案 建立並移動到專案資料夾 建立虛擬環境: pyenv exec python -m venv .venv 啟動虛擬環境: . .venv/bin/activate ,或 source .venv/bin/activate 安裝 Flask 套件: pip install Flask 在根目錄建立 app.py 設定環境變數: export FLASK_APP=app.py export FLASK_ENV=development (存擋後會自動重啟 app) .gitignore: Python.gitignore Application Factories  起手式: # app.py from flask import Flask, render_template, request def create_app():     app = Flask(__name__)     # do something     return app 執行專案 flask run 在瀏覽器網址列輸入 http://127.0.0.1:5000/ Jinja2 Builtin Filter 套件管理 requirements.txt 格式: Requirement Specifiers 、 Version specifiers 範例: EXAMPLE REQUIREMENTS FILE 列出已安裝的套件: pip freeze 連接資料庫 mongodb 安裝套件(先確定有啟動 venv): pip install pymongo or support for mongodb+srv:// URIs requires dnspython: pip install pymongo[srv] PyMongo 3.12....