1.django和flask的相同點和不一樣點?css
共同點:都是基於wsgi的 不一樣點: (1) django是一個大而全的框架,提供了方便內置的框架,orm,admin,分頁,form,model_form,緩存,信號等不少方便的組件, 咱們只要在配置文件中一修改就可使用。 (2) flask是一個短小精悍,可擴展性很是強,flask適合開發小型的網站,也能夠開發大型網站,由於它給咱們提供有許多第三方組件,咱們就能夠結合這些第三方組件集成一個像django同樣擁有不少功能的web框架。可定製性很是強。 (3) flask和django最大的不一樣點:request/session flask是直接導入的,request在全局起做用。 django是依附於request參數,經過參數傳導。 兩個框架沒有優劣之分,具體應用看實際需求。
2.什麼是wsgi?html
web服務網關接口,wsgi是一個協議,實現該寫一個的模塊: - wsgiref - werkzeug 實現協議的模塊本質上就是socket服務端用於接收用戶請求,並處理。 通常web框架基於wsgi實現,這樣實現關注點分離,主要負責業務處理。
1 from wsgiref.simple_server import make_server 2 3 def run_server(environ, start_response): 4 start_response('200 OK', [('Content-Type', 'text/html')]) 5 return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] 6 7 8 if __name__ == '__main__': 9 httpd = make_server('127.0.0.1', 8000, run_server) 10 httpd.serve_forever()
1 from werkzeug.wrappers import Response 2 from werkzeug.serving import run_simple 3 4 def run_server(environ, start_response): 5 response = Response('hello') 6 return response(environ, start_response) 7 8 if __name__ == '__main__': 9 run_simple('127.0.0.1', 8000, run_server)
from werkzeug.wrappers import Response from werkzeug.serving import run_simple class Flask(object): def __call__(self,environ, start_response): response = Response('hello') return response(environ, start_response) def run(self): run_simple('127.0.0.1', 8000, self) app = Flask() if __name__ == '__main__': app.run()
安裝:前端
pip3 install flask
特色: 短小精悍、可擴展強 的一個Web框架。python
特點:上下文管理機制
wsgi:web service getway interface web服務網管接口
依賴wsgi:werkzurgmysql
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple
def run(environ,start_response):
response = Response('hello')
return response(environ, start_response)
if __name__ == '__main__':
run_simple('localhost', 4000, run)
from werkzeug.wrappers import Request, Response @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': from werkzeug.serving import run_simple run_simple('localhost', 4000, hello)
1.使用:全部配置都在app.config中web
PS: 因爲Config對象本質上是字典,因此還可使用app.config.update(...)正則表達式
class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True
2.實現原理
指定一個字符串(類的路徑/文件路徑),importlib-->getattr 找到這個類/模塊把這個類/模塊的全部字段(大寫)一個一個獲取,獲取的時候判斷isupper(),給一個路徑'settings.Foo',能夠找到類並獲取其中大寫的靜態字段。sql
import importlib path = 'settings.Foo' p,c = path.rsplit('.',maxsplit=1) m = importlib.import_module(p) # m = __import__(p) cls = getattr(m,c) for key in dir(cls): if key.isupper(): print(key,getattr(cls,key))
'ENV': None, 'DEBUG': None, 是否開啓DEBUG模式 'TESTING': False, 是否開啓測試模式 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093, })
重點:基於裝飾器實現數據庫
技術點:-functools.wraps(func):保留原函數的原信息django
裝飾器(帶參數)
自定義裝飾器放下面
注意事項:
# -*- coding: utf-8 -*- """ @Datetime: 2018/12/21 @Author: Zhang Yafei """ """1.裝飾器""" from functools import wraps def auth(func): @wraps(func) # 假裝的更完全 def inner(*args,**kwargs): print('前') ret = func(*args,**kwargs) print('後') return ret return inner @auth def index(): print('index') @auth def detail(): print('index') print(index.__name__) print(detail.__name__) """2.endpoint默認是函數名"""
路由設置的兩種方式
方式一 @app.route('/xxx') def index(): return "index" 方式二 def index(): return "index" app.add_url_rule("/xxx",None,index)
-動態路由 /index/<int:nid> def index(nid): print(nid) return 'index'
1 rule, URL規則 2 view_func, 視圖函數名稱 3 endpoint=None, 名稱,用於反向生成URL,即: url_for('名稱') 4 methods=None, 容許的請求方式,如:["GET","POST"] 5 strict_slashes=None, 對URL最後的 / 符號是否嚴格要求, 6 redirect_to=None, 重定向到指定地址 7 8 defaults=None, 默認值,當URL中無參數,函數須要參數時,使用defaults={'k':'v'}爲函數提供參數 9 subdomain=None, 子域名訪問
from flask import Flask,url_for app = Flask(__name__) # 步驟一:定製類 from werkzeug.routing import BaseConverter class RegexConverter(BaseConverter): """ 自定義URL匹配正則表達式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配時,匹配成功後傳遞給視圖函數中參數的值 :param value: :return: """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL時,傳遞的參數通過該方法處理,返回的值用於生成URL中的參數 :param value: :return: """ val = super(RegexConverter, self).to_url(value) return val # 步驟二:添加到轉換器 app.url_map.converters['reg'] = RegexConverter """ 1.用戶發送請求 2.flask內部進行正則匹配 3.調用to_python(正則匹配的結果)方法 4.to_python方法的返回值會交給視圖的函數 """ # 步驟三:使用自定義正則 @app.route('/index/<reg("\d+"):nid>') def index(nid): print(nid,type(nid)) print(url_for('index',nid=987)) return 'index' if __name__ == '__main__': app.run()
@main.route('/data_list/<table_name>') def data_list(table_name): df = reader.enable_admins[table_name] page_obj = Pagination(df['data'].shape[0], request.args.get('p'),per_page_num=5) page_str = page_obj.page_str(base_url=url_for('main.data_list', table_name=table_name)) admin_class = df['admin_class'] data_list = df['data'][page_obj.start:page_obj.end] context = {'table_name':table_name, 'data_list':data_list, 'page_obj':page_obj, 'page_str':page_str, 'admin_class':admin_class, } return render_template('data_list.html', **context)
視圖:FBV和CBV
技術點:反射
視圖函數中獲取request或session 方式一:直接找LocalStack獲取 from flask.globals import _request_ctx_stack print(_request_ctx_stack.top.request.method) 方式二:經過代理LocalProxy(小東北)獲取 from flask import Flask,request print(request.method)
import functools from flask import Flask,views app = Flask(__name__) def wrapper(func): @functools.wraps(func) def inner(*args,**kwargs): return func(*args,**kwargs) return inner class UserView(views.MethodView): methods = ['GET'] decorators = [wrapper,] def get(self,*args,**kwargs): return 'GET' def post(self,*args,**kwargs): return 'POST' app.add_url_rule('/user',None,UserView.as_view('uuuu')) if __name__ == '__main__': app.run()
技術點:面向對象的封裝
request屬性
# 請求相關信息 # request.method 請求方法 GET/POST # request.args GET請求參數 # request.form POST請求參數 # request.values GET和POST請求參數 # request.cookies cookie值 # request.headers 請求頭信息 # request.path 請求地址路徑 # request.full_path 請求地址全路徑 # request.script_root # request.url # request.base_url # request.url_root # request.host_url # request.host # request.files # obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename))
print('headers:', request.headers) print('cookies:', request.cookies) print('path:', request.path) print('full_path:', request.full_path) print('url:', request.url) print('script_root:', request.script_root) print('base_url:', request.base_url) print('url_root:', request.url_root) print('host:', request.host) print('host_url:', request.host_url) headers: Host: 172.25.0.246:8999 Connection: keep-alive Content-Length: 37 Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 S afari/601.1 wechatdevtools/1.02.1901230 MicroMessenger/6.7.3 Language/zh_CN webview/ token/a46ac17280ff1fcb82b684b2084ee168 Origin: http://127.0.0.1:23672 Authorization: f3c4e30debf4030ace5c3cdf40e6332d#1 Content-Type: application/x-www-form-urlencoded Accept: */* Referer: https://servicewechat.com/wx49bec3712cb29bf5/devtools/page-frame.html Accept-Encoding: gzip, deflate cookies: {} path: /api/member/check-reg full_path: /api/member/check-reg? url: http://172.25.0.246:8999/api/member/check-reg script_root: base_url: http://172.25.0.246:8999/api/member/check-reg url_root: http://172.25.0.246:8999/ host: 172.25.0.246:8999 host_url: http://172.25.0.246:8999/
@main.route('/data_list/<table_name>') def data_list(table_name): df = reader.enable_admins[table_name] page_obj = Pagination(df['data'].shape[0], request.args.get('p'),per_page_num=5) page_str = page_obj.page_str(base_url=url_for('main.data_list', table_name=table_name)) admin_class = df['admin_class'] data_list = df['data'][page_obj.start:page_obj.end] context = {'table_name':table_name, 'data_list':data_list, 'page_obj':page_obj, 'page_str':page_str, 'admin_class':admin_class, } return render_template('data_list.html', **context)
@ac.route('/login',methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') username = request.form.get('username') password = request.form.get('pwd') """數據庫驗證""" password = get_md5(password) # conn = Connect(host='localhost', user='root', password='0000', database='flask_code', charset='utf8') # cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 每一行是字典 # cursor.execute("select id,username,nickname from userinfo where username=%(us)s and password=%(pw)s",{'us':username,'pw':password}) # data = cursor.fetchone() data = fetchone("select id,username,nickname from userinfo where username=%(us)s and password=%(pw)s",{'us':username,'pw':password}) if not data: return render_template('login.html',error='用戶名或密碼錯誤') session['user_info'] = {'id':data['id'],'username':data['username'],'nickname':data['nickname']} if request.form.get('remember'): session.permanent = True ac.permanent_session_lifetime = timedelta(days=31) return redirect('/home')
技術點:面向對象的封裝
響應體:4種
return '歡迎使用' return jsonify({'k1':'v1'}) return render_template('xxx.html') return redirect('/index')
定製響應頭
obj = make_response(render_template('index.html')) obj.headers['xxxxx'] = '123' obj.set_cookie('key','value') return obj
示例程序:用戶訪問限制
@app.route('/detail/<int:nid>') def detail(nid): if not session.get('user'): return redirect(url_for('login')) info = STUDENT_DICT[nid] return render_template('detail.html', info=info)
def auth(func): @wraps(func) def inner(*args,**kwargs): if not session.get('user'): return redirect(url_for('login')) ret = func(*args,**kwargs) return ret return inner @app.route('/index') @auth def index(): return render_template('index.html',stu_dict=STUDENT_DICT) 應用場景:比較少的函數中須要添加額外的功能
@app.before_request def xxxx(): if request.path == '/login': return None if not session.get('user'): return None return redirect(url_for('login')) 應用場景:比較多的函數中須要添加額外的功能
基本數據類型:能夠執行python語法,如:dict.get() list['xx']
控制代碼塊:條件語句和循環語句
- if/else if /else / endif - for / endfor
<ul class="pagination"> {% if page_obj.num_pages > 1 %} {{ page_str }} {% else %} {% endif %} </ul>
<table class="table table-bordered"> <thead> <tr style="background-color: pink"> <th>序號</th> <th>文件名</th> <th>樣本數</th> <th>特徵數</th> <th>操做</th> </tr> </thead> <tbody> {% for k,v in data_list.items() %} <tr> <td>{{ loop.index }}</td> <td><a href="{{ url_for('main.data_list',table_name=k)}}">{{ k }}</a></td> <td>{{ v['data'].shape }}</td> <td>{{ v['data'].shape }}</td> <td><a href="{{ url_for('main.data_list',table_name=k)}}">查看</a></td> </tr> {% endfor %} </tbody> </table>
除此以外,還有一個特殊的循環函數loop
在循環內部,你可使用一個叫作loop的特殊變量來得到關於for循環的一些信息
好比:要是咱們想知道當前被迭代的元素序號,並模擬Python中的enumerate函數作的事情,則可使用loop變量的index屬性,例如:
{% for post in posts%} {{loop.index}}, {{post.title}} {% endfor %}
會輸出這樣的結果 1, Post title2, Second Post
{% for post in posts%} {{loop.cycle('odd','even')}} {{post.title}} {% endfor %} 會輸出這樣的結果: odd Post Title even Second Post
傳入函數
自定義函數
@app.template_global() def sb(a,b): return a+b @app.template_filter() # 適合if 判斷 def db(a,b,c): return a+b+c
html {{ 1|db(2,3) }} {% if 1|db(2,3) %} <div>666</div> {% else %} <div>999</div> {% endif %} @app.template_filter() # 適合if 判斷 def db(a,b,c): return a+b+c
用裝飾器來實現自定義過濾器。裝飾器傳入的參數是自定義的過濾器名稱。 @app.template_filter('lireverse') def do_listreverse(li): # 經過原列表建立一個新列表 temp_li = list(li) # 將新列表進行返轉 temp_li.reverse() return temp_li 在 html 中使用該自定義過濾器 <br/> my_array 原內容:{{ my_array }}<br/> my_array 反轉:{{ my_array | lireverse }} 運行結果 my_array 原內容:[3, 4, 2, 1, 7, 9] my_array 反轉:[9, 7, 1, 2, 4, 3]
模板繼承
layout.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% block content %}{% endblock %} </body> </html> tpl.html {% extends 'layout.html' %} {% block content %} {% include 'form.html' %} {% macro ccccc(name,type='text', value='') %} <h1>宏</h1> <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> <input type="submit" name="提交"> {% endmacro %} {{ ccccc('n1') }} {{ ccccc('n2') }} {% endblock %}
<include 'from.html'> form.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>sasasasa sasasasasasa</div> </body> </html>
宏
{% macro ccccc(name,type='text', value='') %} <h1>宏</h1> <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> <input type="submit" name="提交"> {% endmacro %} {{ ccccc('n1') }} {{ ccccc('n2') }}
安全
前端:{{ txt|safe }}
後端:Markup(txt)
原理:加密後放置在用戶瀏覽器的cookie中
流程:
請求到來:flask讀取cookie中的session對應的值:eyJrMiI6NDU2LCJ1c2VyIjoiZmVpIn0,將該值解密並反序列化成字典,放入內存以便視圖函數使用。 視圖函數: @app.route('/sess') def sess(): print(session.__class__) session['k1'] = 123 session['k2'] = 456 del session['k1'] return 'Session' 請求結束,flask會讀取內存中字典的值,進行序列化+加密,寫入用戶cookie中。
實現原理(源碼)
def wsgi_app(self, environ, start_response): """ 1.獲取environ並對其進行再次封裝 2.從environ中獲取名稱爲session的cookie,解密,反序列化 3.兩個東西放到‘某個神奇'的地方 """ # ctx = RequestContext(self, environ) #self是app對象,evviron是原始數據對象 # ctx.request = Request(environ) # ctx.session = None ctx = self.request_context(environ) error = None try: try: ctx.push() # 4. 執行視圖函數 # 5.'某個神奇’獲取session,加密,序列化,寫入cookie response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None """ 6.'某個神奇'位置清空 """ ctx.auto_pop(error)
閃現(flash): 在session存儲一個數據,讀取時經過pop將數據刪除,造成一種數據只能取一次的效果
from flask import Flask,flash,get_flashed_messages @app.route('/page1') def page1(): flash('臨時數據存儲','error') flash('sasasasa','error') flash('sasasas','info') return 'Session' @app.route('/page2') def page2(): print(get_flashed_messages(category_filter=['error'])) return 'Session'
通常不經常使用,由於它的執行順序很靠前,無序獲取request對象,與請求相關信息難以得到
-用戶發起請求時,才執行
class Middleware(object): def __init__(self,old): self.old = old def __call__(self, *args, **kwargs): print('前') ret = self.old(*args,**kwargs) print('後') return ret if __name__ == '__main__': app.wsgi_app = Middleware(app.wsgi_app) app.run()
技術點:before_request和after_request的實現原理
六大裝飾器
before_first_request before_request 視圖函數以前,原理是將視圖函數放入到一個列表中,循環,若是有返回值中止循環,後面的函數也將不會執行 after_request 視圖函數以後,原理是將視圖函數放入到一個列表中reverse,循環執行 template_global template_filter errorhandler @app.errorhandler(404) def not_found(arg): print(arg) return '404 沒找到'
# -*- coding: utf-8 -*- """ @Datetime: 2018/12/21 @Author: Zhang Yafei """ from flask import Flask app = Flask(__name__) @app.before_first_request def x(): print('before_first') @app.before_request def x1(): print('before:x1') @app.before_request def xx1(): print('before:xx1') @app.after_request def x2(response): print('after:x2') return response @app.after_request def xx2(response): print('after:xx2') return response @app.route('/index') def index(): print('index') return 'index' @app.route('/order') def order(): print('order') return 'order' @app.errorhandler(404) def not_found(arg): print(arg) return '404 沒找到' if __name__ == '__main__': app.run()
(1) 目標:目錄結構的劃分
(2) 自定義模板、靜態文件
admin = Blueprint( 'admin', __name__, template_folder='templates', static_folder='static' )
(3) 給某一類url添加前綴變量 app.register_blueprint(admin, url_prefix='/admin')
(4) 給一類url添加before_request
@app.before_request def x1(): print('app.before_request')
from flask import Flask from werkzeug.wsgi import DispatcherMiddleware from werkzeug.serving import run_simple app1 = Flask("app1") app1.config['DB'] = 123 app2 = Flask("app2") app1.config['DB'] = 456 @app1.route('/web') def web(): print('web') return '12213' @app1.route('/news') def news(): print('news') return '12213' @app2.route('/admin') def admin(): print('admin') return '12213' @app2.route('/article') def article(): print('article') return '12213' """ /web /new /app2/admin /app2/article """ app = DispatcherMiddleware(app1, { '/app2': app2, }) if __name__ == '__main__': run_simple(hostname='127.0.0.1', port=5000, application=app)
from multi_app import app1 from multi_app import app2 with app1.app_context(): pass # 爲app1建立數據庫 with app2.app_context(): pass # 爲app2建立數據庫
技術點:@media
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" /> </head> <body> <div class="row" style="background-color: #28a4c9"> <div class="col-lg-6">.col-lg-6</div> <div class="col-lg-6">.col-lg-6</div> </div> <div class="row" style="background-color: #67b168"> <div class="col-md-6">.col-md-6</div> <div class="col-md-6">.col-md-6</div> </div> <div class="row" style="background-color: red"> <div class="col-sm-6">.col-sm-6</div> <div class="col-sm-6">.col-sm-6</div> </div> <div class="row" style="background-color: gold"> <div class="col-xs-6">.col-xs-6</div> <div class="col-xs-6">.col-xs-6</div> </div> </body> </html>
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> body{ margin: 0; } .pg{ width: 100%; background-color: rebeccapurple; } @media (min-width: 666px) { .pg{ background-color: green; } } @media (min-width: 888px) { .pg{ background-color: red; } } </style> </head> <body> <div> <div class="pg">asdfasdf</div> </div> </body> </html>
注:
1. 上下文管理的實現? 當請求到來的時候, flask會把請求相關和session相關信息封裝到一個ctx = RequestContext對象中, 會把app和g封裝到app_ctx = APPContext對象中去, 經過localstack對象將ctx、app_ctx對象封裝到local對象中 獲取數據(執行視圖函數的時候) 經過導入一個模塊,用模塊.的方式獲取咱們想要的數據 實現細節: 經過localproxy對象+偏函數,調用localstack去local中獲取對應的響應ctx、app_ctx中封裝值 問題:爲何要把ctx = request/session app_ctx = app/g 由於離線腳本須要使用app_ctx 請求結束 調用localstk的pop方法,將ctx和app_ctx移除 2. 爲何把上下文管理分紅: - 應用上下文:request/session - 請求上下文: app/g 離線腳本應用 3. Local的做用? 相似於threading.local的做用,可是是他的升級版(greentlet.get_current()) __storage__ = { 1231: {}, 1231: {} } 4. LocalStack做用? 將Local中__storage__的值維護成一下結構: __storage__ = { 1231: {stack:[],}, 1231: {stack:[],} } 5. 爲何要維護成一個棧? 6. 爲何導入request,就可使用? 每次執行request.xx方法時,會觸發LocalProxy對象的__getattr__等方法,由方法每次動態的使用 LocalStack去Local中獲取數據