Flask基礎部分
Flask目錄結構(藍圖)
views中存放藍圖,每個藍圖也可以有自己的模板,用藍圖對不同功能的視圖函數進行隔離,類似於django中的app
pro_flask包的init.py文件, 用於注冊所有的藍圖
from flask import Flask from pro_flask.views.user import user from pro_flask.views.blog import blog app = Flask(__name__, template_folder='templates') app.register_blueprint(user) app.register_blueprint(blog) app.secret_key = "alex"
manage.py文件,作為整個項目的啟動文件
from pro_flask import app from flask_script import Manager from flask_bootstrap import Bootstrap Bootstrap(app) manage = Manager(app) if __name__ == '__main__': # app.__call__() manage.run()
views包中的blog.py,必須要通過session驗證才能訪問,否則回到登錄界面
from flask import Blueprint, session, redirect, url_for, render_template blog = Blueprint("blog", __name__, template_folder='templates') @blog.route("/index/") def index(): return render_template("index.html") @blog.before_request # 請求該藍圖中所有的函數時都會先走這兒! def process_request(): val = session.get("login") if val: return None else: return redirect(url_for("user.login")) # 如果沒有session則阻斷請求
views包中的user.py,定義一些與用戶相關的視圖函數
from flask import Blueprint, render_template, request, session, url_for, redirect from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import DataRequired, EqualTo, Length, ValidationError user = Blueprint("user", __name__, template_folder='templates') @user.route("/login/", methods=["GET", "POST"]) def login(): if request.method == "POST": username = request.form.get("username") password = request.form.get("password") if username == "alex" and password == "123": session["name"] = "alex" session["password"] = "123" session["login"] = 1 return redirect(url_for("blog.index")) return render_template("login.html") @user.route("/logout/") def logout(): session.pop("login") return render_template("base.html") class Register(FlaskForm):
# username為表單中input中name屬性對應的值, "用戶名"為label的內容, balidators為驗證器(寫一些驗證表單內容的規則) username = StringField("用戶名", validators=[DataRequired("用戶名不能為空"), # 表單的驗證器 Length(min=6, max=12, message="長度需要在6~12個字符之間")]) password = PasswordField("密碼", validators=[DataRequired("密碼不能為空"), Length(min=6, max=12, message="密碼長度需要在6~12個字符之間")]) confirm = PasswordField("確認密碼", validators=[DataRequired("密碼不能為空"), EqualTo("password", message="兩次輸入的密碼不一致")]) submit = SubmitField("注冊") def validate_username(self, field): if self.username.data == "alex": raise ValidationError("該用戶已存在") @user.route("/register/", methods=["GET", "POST"]) def register(): form = Register() if form.validate_on_submit(): return redirect(url_for("user.login")) return render_template("register.html", form=form)
加載靜態資源的方法
{{ url_for('static',filename='路徑/文件名稱.css/js/jpg') }}
實例:
{% block metas %} {{ super() }} <link rel="icon" href="{{ url_for('static',filename='img/1.jpeg',_external=True) }}"> {% endblock %}
Flask原碼部分理解
app.run()方法,負責啟動項目,監聽請求,那么它在內部做了哪些操作呢???
def run(self): ...
from werkzeug.serving import run_simple
run_simple(host, port, self, **options) # 第三個參數self指的是app這個對象自己
...
以上是原碼中app的run方法的部分內容,其實它執行的是run_simple這個方法
第三個參數將被反射調用,那么app(),就是去執行了app.__call__()方法
可以看出__call__方法其實是在調用wsgi_app這個接口
那么也就是說,將請求相關的所有內容都封裝到了一個類的對象中
ctx = self.request_context(environ) # 此時的ctx就是請求的對象
跟進_request_ctx_stack
可以看出將那個請求對象push進了這個LocalStack類的對象里,跟進到LocalStack()中
找到這個push方法,這里的obj就是那個請求的對象
_local是在類初始化時,實例化的Local對象,反射這個Local對象中是否有stack這個屬性或方法,如果沒有,則執行_local.stack = [], rv = []
在rv列表中把那個請求對象加進來,而_local.stack = [],會觸發Local里面的__setattr__方法,所以跟進到Local中查看,Local的實現原理類似於threading.Local
try:
from greenlet import getcurrent as get_ident # 如果有協程庫,那么就支持協程
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident # 支持線程
class Local(object): __slots__ = ('__storage__', '__ident_func__') def __init__(self): object.__setattr__(self, '__storage__', {}) # 使用這種方法不會觸發下面的__setattr__方法,避免遞歸 object.__setattr__(self, '__ident_func__', get_ident)# 將獲取唯一協程標識的方法賦值給了__ident_func__
def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: storage[ident][name] = value # 為每個協程/線程開辟空間,如果未開辟 則執行下面的代碼 except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)