django:大而全的全的框架,重武器;內置不少組件:ORM、admin、Form、ModelForm、中間件、信號、緩存、csrf等
flask: 微型框架、可擴展強,若是開發簡單程序使用flask比較快速,
若是實現負責功能就須要引入一些組件:flask-session/flask-SQLAlchemy/wtforms/flask-migrate/flask-script/blinker
這兩個框架都是基於wsgi協議實現的,默認使用的wsgi模塊不同。
還有一個顯著的特色,他們處理請求的方式不一樣:
django: 經過將請求封裝成Request對象,再經過參數進行傳遞。
flask:經過上下文管理實現。
延伸:
- django組件
- flask組件,用途
- wsgi
- 上下文管理
wsgi 》》點我html
新知識python
web服務網關接口,一套協議。 實現了wsgi協議的模塊本質上就是編寫了socket服務端,用來監聽用戶請求,若是有請求到來,則將請求進行一次封裝, 而後將【請求】交給 web框架來進行下一步處理。 目前接觸: wsgiref (dajngo) werkzurg (flask) uwsgi from wsgiref.simple_server import make_server def run_server(environ, start_response): """ environ: 封裝了請求相關的數據 start_response:用於設置響應頭相關數據 """ start_response('200 OK', [('Content-Type', 'text/html')]) return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] if __name__ == '__main__': httpd = make_server('', 8000, run_server) httpd.serve_forever()
Django源碼:jquery
class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super(WSGIHandler, self).__init__(*args, **kwargs) self.load_middleware() def __call__(self, environ, start_response): # 請求剛進來以後 # set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) request = self.request_class(environ) response = self.get_response(request) response._handler_class = self.__class__ status = '%d %s' % (response.status_code, response.reason_phrase) response_headers = [(str(k), str(v)) for k, v in response.items()] for c in response.cookies.values(): response_headers.append((str('Set-Cookie'), str(c.output(header='')))) start_response(force_str(status), response_headers) if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'): response = environ['wsgi.file_wrapper'](response.file_to_stream) return response
a. wsgi, 建立socket服務端,用於接收用戶請求並對請求進行初次封裝。
b. 中間件,對全部請求到來以前,響應以前定製一些操做。
c. 路由匹配,在url和視圖函數對應關係中,根據當前請求url找到相應的函數。
d. 執行視圖函數,業務處理【經過ORM去數據庫中獲取數據,再去拿到模板,而後將數據和模板進行渲染】
e. 再通過全部中間件
f. 經過wsgi將響應返回給用戶。web
圖ajax
全部的請求作統一操做時,用中間件redis
中間件點我 sql
全部方法: - process_request - process_view - process_template_response , 當視圖函數的返回值對象中有render方法時,該方法纔會被調用。 - process_response - process_excaption
- 登陸驗證,爲何:若是不適用就須要爲每一個函數添加裝飾器,太繁瑣。 - 權限處理,爲何:用戶登陸後,將權限放到session中,而後再每次請求時須要判斷當前用戶是否有權訪問當前url,這檢查的東西就能夠放到中間件中進行統一處理。 - 還有一些內置: - csrf,爲何 - session,爲何 - 全站緩存 ,爲何 - 另外,還有一個就是處理:跨域 (先後端分離時,本地測試開發時使用的。)
目標:防止用戶直接向服務端發起POST請求。
方案:先發送GET請求時,將token保存到(csrftoken保存位置):cookie、Form表單中(保存位置隱藏的input標籤),之後再發送請求時只要攜帶過來便可。
數據庫
問題:如何向後臺發送POST請求?django
新知識json
form表單提交: <form method="POST"> {% csrf_token %} <input type='text' name='user' /> <input type='submit' /> </form> ajax提交: $.ajax({ url:'/index', type:'POST', data:{csrfmiddlewaretoken:'{{ csrf_token }}',name:'alex'} }) 前提:引入jquery + 引入jquery.cookie (此模塊能夠完成從cookie中取出csrftoken) $.ajax({ url: 'xx', type:'POST', data:{name:'oldboyedu'}, headers:{ X-CSRFToken: $.cookie('csrftoken') }, #在請求頭中加上這個信息 注意名字必須是 X-CSRFToken: dataType:'json', // arg = JSON.parse('{"k1":123}') success:function(arg){ } })
<body> <input type="button" onclick="Do1();" value="Do it"/> <input type="button" onclick="Do2();" value="Do it"/> <input type="button" onclick="Do3();" value="Do it"/> <script src="/static/jquery-3.3.1.min.js"></script> <script src="/static/jquery.cookie.js"></script> //導入jquery.cookie <script> $.ajaxSetup({ //在之後每個發送ajax請求以前,每次發送以前 都會執行該函數
beforeSend: function(xhr, settings) { xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken')); } }); function Do1(){ $.ajax({ url:"/index/", data:{id:1}, type:'POST', success:function(data){ console.log(data); } }); } function Do2(){ $.ajax({ url:"/index/", data:{id:1}, type:'POST', success:function(data){ console.log(data); } }); } function Do3(){ $.ajax({ url:"/index/", data:{id:1}, type:'POST', success:function(data){ console.log(data); } }); } </script> </body>
FBV: def index(request): pass CBV: class IndexView(View): # 若是是crsf相關,必須放在此處 def dispach(self,request): # 經過反射執行post/get @method_decoretor(裝飾器函數) def get(self,request): pass def post(self,request): pass 路由:IndexView.as_view()
FBV和CBV的區別?
- 沒什麼區別,由於他們的本質都是函數。CBV的.as_view()返回的view函數,view函數中調用類的dispatch方法,在dispatch方法中經過反射執行get/post/delete/put等方法。
- CBV比較簡潔,GET/POST等業務功能分別放在不一樣get/post函數中。FBV本身作判斷進行區分。
在cbv中加裝飾器:
1.裝飾器
from django.views import View from django.utils.decorators import method_decorator def auth(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner class UserView(View): @method_decorator(auth) def get(self,request,*args,**kwargs):
2.處理csrf的問題
from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator # 方式1 # @method_decorator(csrf_exempt,name="dispatch") class UserView(View): #方式二 #也能夠自定義以dispatch()方法 @method_decorator(csrf_exempt) #加上這句就能夠不驗證post請求的csrf內容 def dispatch(self, request, *args, **kwargs): #寫上一個print 方法 print('Hollow') #運行的內容繼承dispatch 接收返回值 res=super(UserView, self).dispatch(request, *args, **kwargs) #將返回值返回 return res #定義兩個類,get post這兩個 def get(self,request): print('get11111') return HttpResponse('getOK') def post(self,request): return HttpResponse('postOK')
a. 增刪改查
b. 經常使用
order_by
group_by
limit
練表/跨表
c. 靠近原生SQL (如何在orm中執行原生sql)
- extra def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # 構造額外的查詢條件或者映射,如:子查詢 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) - raw def raw(self, raw_query, params=None, translations=None, using=None): # 執行原生SQL models.UserInfo.objects.raw('select * from userinfo') # 若是SQL是其餘表時,必須將名字設置爲當前UserInfo對象的主鍵列名 models.UserInfo.objects.raw('select id as nid,name as title from 其餘表') # 爲原生SQL設置參數 models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) # 將獲取的到列名轉換爲指定列名 name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) # 指定數據庫 models.UserInfo.objects.raw('select * from userinfo', using="default") - 原生 from django.db import connection, connections cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1]) row = cursor.fetchone() # fetchall()/fetchmany(..)
d. 高級一點
- F
- Q
- select_related
- prefech_related
e. 其餘:
################################################################## # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET # ################################################################## def all(self) # 獲取全部的數據對象 def filter(self, *args, **kwargs) # 條件查詢 # 條件能夠是:參數,字典,Q def exclude(self, *args, **kwargs) # 條件查詢 # 條件能夠是:參數,字典,Q def select_related(self, *fields) 性能相關:表之間進行join連表操做,一次性獲取關聯的數據。 model.tb.objects.all().select_related() model.tb.objects.all().select_related('外鍵字段') model.tb.objects.all().select_related('外鍵字段__外鍵字段') def prefetch_related(self, *lookups) 性能相關:多表連表操做時速度會慢,使用其執行屢次SQL查詢在Python代碼中實現連表操做。 # 獲取全部用戶表 # 獲取用戶類型表where id in (用戶表中的查到的全部用戶ID) models.UserInfo.objects.prefetch_related('外鍵字段') from django.db.models import Count, Case, When, IntegerField Article.objects.annotate( numviews=Count(Case( When(readership__what_time__lt=treshold, then=1), output_field=CharField(), )) ) students = Student.objects.all().annotate(num_excused_absences=models.Sum( models.Case( models.When(absence__type='Excused', then=1), default=0, output_field=models.IntegerField() ))) def annotate(self, *args, **kwargs) # 用於實現聚合group by查詢 from django.db.models import Count, Avg, Max, Min, Sum v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')) # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1) # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1) # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 def distinct(self, *field_names) # 用於distinct去重 models.UserInfo.objects.values('nid').distinct() # select distinct nid from userinfo 注:只有在PostgreSQL中才能使用distinct進行去重 def order_by(self, *field_names) # 用於排序 models.UserInfo.objects.all().order_by('-id','age') def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # 構造額外的查詢條件或者映射,如:子查詢 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) def reverse(self): # 倒序 models.UserInfo.objects.all().order_by('-nid').reverse() # 注:若是存在order_by,reverse則是倒序,若是多個排序則一一倒序 def defer(self, *fields): models.UserInfo.objects.defer('username','id') 或 models.UserInfo.objects.filter(...).defer('username','id') #映射中排除某列數據 def only(self, *fields): #僅取某個表中的數據 models.UserInfo.objects.only('username','id') 或 models.UserInfo.objects.filter(...).only('username','id') def using(self, alias): 指定使用的數據庫,參數爲別名(setting中的設置) ################################################## # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # ################################################## def raw(self, raw_query, params=None, translations=None, using=None): # 執行原生SQL models.UserInfo.objects.raw('select * from userinfo') # 若是SQL是其餘表時,必須將名字設置爲當前UserInfo對象的主鍵列名 models.UserInfo.objects.raw('select id as nid from 其餘表') # 爲原生SQL設置參數 models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) # 將獲取的到列名轉換爲指定列名 name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) # 指定數據庫 models.UserInfo.objects.raw('select * from userinfo', using="default") ################### 原生SQL ################### from django.db import connection, connections cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1]) row = cursor.fetchone() # fetchall()/fetchmany(..) def values(self, *fields): # 獲取每行數據爲字典格式 def values_list(self, *fields, **kwargs): # 獲取每行數據爲元祖 def dates(self, field_name, kind, order='ASC'): # 根據時間進行某一部分進行去重查找並截取指定內容 # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日) # order只能是:"ASC" "DESC" # 並獲取轉換後的時間 - year : 年-01-01 - month: 年-月-01 - day : 年-月-日 models.DatePlus.objects.dates('ctime','day','DESC') def datetimes(self, field_name, kind, order='ASC', tzinfo=None): # 根據時間進行某一部分進行去重查找並截取指定內容,將時間轉換爲指定時區時間 # kind只能是 "year", "month", "day", "hour", "minute", "second" # order只能是:"ASC" "DESC" # tzinfo時區對象 models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC) models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) """ pip3 install pytz import pytz pytz.all_timezones pytz.timezone(‘Asia/Shanghai’) """ def none(self): # 空QuerySet對象 #################################### # METHODS THAT DO DATABASE QUERIES # #################################### def aggregate(self, *args, **kwargs): # 聚合函數,獲取字典類型聚合結果 from django.db.models import Count, Avg, Max, Min, Sum result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid')) ===> {'k': 3, 'n': 4} def count(self): # 獲取個數 def get(self, *args, **kwargs): # 獲取單個對象 def create(self, **kwargs): # 建立對象 def bulk_create(self, objs, batch_size=None): # 批量插入 # batch_size表示一次插入的個數 objs = [ models.DDD(name='r11'), models.DDD(name='r22') ] models.DDD.objects.bulk_create(objs, 10) def get_or_create(self, defaults=None, **kwargs): # 若是存在,則獲取,不然,建立 # defaults 指定建立時,其餘字段的值 obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2}) def update_or_create(self, defaults=None, **kwargs): # 若是存在,則更新,不然,建立 # defaults 指定建立時或更新時的其餘字段 obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1}) def first(self): # 獲取第一個 def last(self): # 獲取最後一個 def in_bulk(self, id_list=None): # 根據主鍵ID進行查找 id_list = [11,21,31] models.DDD.objects.in_bulk(id_list) def delete(self): # 刪除 def update(self, **kwargs): # 更新 def exists(self):
- request/bs4 - requests模塊 - 參數: - url - headers - cookies - data - json - params - proxy - 返回值: - content - iter_content - text - encoding="utf-8" - cookie.get_dict() - bs4 - 解析:html.parser -> lxml - find - find_all - text - attrs - get - 其餘: 常見請求頭: - user-agent - host - referer - cookie - content-type 套路: - 先給你cookie,而後再給你受權。 - 憑證 輪詢+長輪詢 - scrapy - 高性能相關,單線程併發發送Http請求 - twisted - gevent - asyncio 本質:基於IO多路複用+非阻塞的socket客戶端實現 問題:異步非阻塞? 問題:什麼是協程? - scrapy框架 - scrapy執行流程(包含全部組件) - 記錄爬蟲爬取數據深度(層級),request.meta['depth'] - 傳遞cookie - 手動 - 自動:meta={'cookiejar':True} - 起始URL - 持久化:pipelines/items - 去重 - 調度器 - 中間件 - 下載中間件 - agent - proxy - 爬蟲中間件 - depth - 擴展+信號 - 自定義命令 - scrapy-redis組件,本質:去重、調度器任務、pipeline、起始URL放到redis中。 - 去重,使用的redis的集合。 - 調度器, - redis列表 - 先進先出隊列 - 後進先出棧 - redis有序集合 - 優先級隊列 PS:深度和廣度優先 - pipelines - redis列表 - 起始URL - redis列表 - redis集合 補充: 自定義encoder實現序列化時間等特殊類型: json.dumps(xx,cls=MyEncoder) - scrapy
登陸驗證
權限處理 (session中封裝了用戶的權限信息,根據取出的權限判斷用戶能看到什麼,不能看到什麼)
CSRF
session
cors跨域 : 解決方法
方法三
- 對用戶請求的數據進行校驗
- 生成HTML標籤
方法一:重寫構造方法
from django.shortcuts import render,HttpResponse from app01 import models def index(request): # return HttpResponse("...") return render(request,'index.html',{'x':123}) from django.forms import Form from django.forms import fields class UserForm(Form): name = fields.CharField(label='用戶名',max_length=32) email = fields.EmailField(label='郵箱') ut_id = fields.ChoiceField( # choices=[(1,'二筆用戶'),(2,'悶騷')] choices=[] ) def __init__(self,*args,**kwargs): super(UserForm,self).__init__(*args,**kwargs) self.fields['ut_id'].choices = models.UserType.objects.all().values_list('id','title') def user(request): if request.method == "GET": form = UserForm() return render(request,'user.html',{'form':form})
方法二: 使用ModelChoiceField 而且給數據庫類中添加__str__類
from django.forms import Form from django.forms import fields from django.forms.models import ModelChoiceField #用這個類來給頁面渲染出標籤 class UserForm(Form): name = fields.CharField(label='用戶名',max_length=32) email = fields.EmailField(label='郵箱') #這裏渲染出從數據庫中查出的數據 要這要寫 ut_id = ModelChoiceField(queryset=models.UserType.objects.all())
數據庫中要有此字段
class UserType(models.Model): title = models.CharField(max_length=32) def __str__(self): #這個也要寫上 return self.title
orm中的方法(筆記中有新內容,複習下方法)
將數據保存入指定的數據庫中去 使用magrate來制定(傳統方式)
讀寫分離方法二
setting中的配置
代碼:
db_router.py中的代碼, 此類中規定讀和寫用的數據庫
class Router1: def db_for_read(self, model, **hints): """ Attempts to read auth models go to auth_db. """ return 'db1' def db_for_write(self, model, **hints): """ Attempts to write auth models go to auth_db. """ return 'default'
settings.py中關於數據庫的配置
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'db1': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'), }, } DATABASE_ROUTERS = ['db_router.Router1',] 使用: models.UserType.objects.create(title='VVIP') result = models.UserType.objects.all() print(result)
方法二升級版:粒度更細的方法(粒度到每一張表的讀寫的配置)
(若是有多個app想讓不一樣的app數據放到不一樣的數據庫)
代碼
# 第一步: python manage.py makemigraions # 第二步: app01中的表在default數據庫建立 python manage.py migrate app01 --database=default # 第三步: app02中的表在db1數據庫建立 python manage.py migrate app02 --database=db1
手動操做
(已知usertype是app01中的表)
(已知Users是app02中的表)
m1.UserType.objects.using('default').create(title='VVIP') m2.Users.objects.using('db1').create(name='VVIP',email='xxx')
自動操做
class Router1: def db_for_read(self, model, **hints): """ Attempts to read auth models go to auth_db. """ if model._meta.app_label == 'app01': return 'default' else: return 'db1' def db_for_write(self, model, **hints): """ Attempts to write auth models go to auth_db. """ if model._meta.app_label == 'app01': return 'default' else: return 'db1'
settings中的配置
DATABASE_ROUTERS = ['db_router.Router1',]
class Router1: def allow_migrate(self, db, app_label, model_name=None, **hints): """ All non-auth models end up in this pool. """ if db=='db1' and app_label == 'app02': return True elif db == 'default' and app_label == 'app01': return True else: return False # 若是返回None,那麼表示交給後續的router,若是後續沒有router,則至關於返回True def db_for_read(self, model, **hints): """ Attempts to read auth models go to auth_db. """ if model._meta.app_label == 'app01': return 'default' else: return 'db1' def db_for_write(self, model, **hints): """ Attempts to write auth models go to auth_db. """ if model._meta.app_label == 'app01': return 'default' else: return 'db1'
三: 類中有一個allow_migrate方法來管控是否容許某些app中數據進行遷移到對應的數據庫 (筆記代碼)
注意:若是返回None表示交給後續的router 若是後續沒有router 至關於返回的True(因此通常返回 TURE 或False 不要用None)
a. 什麼是websocket?
websocket是給瀏覽器新建一套協議。協議規定:瀏覽器和服務端鏈接以後不斷開,以此能夠完成:服務端向客戶端主動推送消息。
websocket協議額外作的一些前天操做:
- 握手,鏈接前進行校驗
- 發送數據加密
b. websocket本質
- socket
- 握手,魔法字符串+加密
- 加密,payload_len=127/126/<=125 -> mask key
內置的序列化只能處理queryset類型的數據
19 admin &stark組件
20 Content
21
22 冪等性?
23 webservice是什麼?