Django


數據庫操作

F對象

  1. 通常對數據庫中在不獲取字段值的情況下操作
    1. 有效的解決並發技術的問題
    2. 在更新的時候內部會上一把鎖,使得字段對應值的計算是同步的
  2. 用戶字段之間的比較
  3. F('列名')
  4. from django.db.models import F

Q對象

  1. 專門解決查詢結果集中邏輯或 | 和邏輯非 ~ 邏輯與 & 的操作
  2. from django.db.models import Q
  3. Q('列名')

聚合查詢

  1. from django.db.models import *

  2. 整表聚合

    1. 聚合函數:Sum(),Avg(),Count(),Max(),Min()
    2. XXXX.objects.aggregate(結果變量名=聚合函數('列名')),返回字典
  3. 分組聚合

    1. QuerySet.annotate(結果變量名=聚合函數('列')),返回QuerySet
      1. 先用query_set = XXX.objects.values()查找查詢要分組聚合的列
      2. result = query_set.annotate(結果變量名 = 聚合函數('列'))
    2. 實現having的效果使用filter函數原生數據庫操作(盡量避免,因為容易引起SQL注入)
  4. XXX.objects.raw(sql語句,拼接參數),返回RawQuerySet(必須使用兩個參數,為了避免SQL注入)

  5. from django.db import connection
    with connection.cursor() as cur:
        cur.execute('SQL語句','拼接參數')
    
  6. 級聯刪除

    1. models.CASCADE:一起刪
    2. models.PROTECT:受保護
    3. models.SET_NULL:刪了,關聯設為空
    4. models.SET_DEFUALT:刪了,關聯設為默認值
  7. 反向查詢

    1. 一對一:實例對象.引用類名(小寫),實例對象的類中沒有引用類名的類
    2. 一對多、多對多:實例對象.引用類名(小寫)**_set

調試

  1. 使用python manage.py shell(這樣就可以不用對django設置一堆環境變量了,可以直接在終端操作)

  2. 但是在py文件中調試要記得配置django環境變量(比如需要一些異步的腳本或者初始化腳本的時候)

    import os
    import sys
    import django
    
    base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(base_dir)
    
    # 將配置文件的路徑寫到 DJANGO_SETTINGS_MODULE 環境變量中
    os.environ.setdefault("DJANGO_SETTINGS_MODULE","DJango4WeChat.settings")
    django.setup()
    

會話

cookies和session就是保存會話狀態的存儲技術

cookies(存儲到瀏覽器,相對不安全)

  1. 保存在客戶端瀏覽器上的存儲空間
  2. Application->Storage->Cookies
  3. 鍵值對、ASCII、有生命周期、按域存儲、攜帶到服務器
  4. 設置:HttpResponse.set_cookies(key,value='',max_ages='',expire='')
  5. 獲取:request.COOKIES.get(key,'默認值')
  6. 刪除:HttpRespone.delet_cookie(key)

session(存儲到服務器,相對安全)

  1. 用於在服務器上開辟一段空間用於保留瀏覽器和服務器交互時的重要數據
  2. 實現方式
    1. 使用session需要在瀏覽器客戶端啟用cookie,且在cookie中存儲sessionID
    2. 每一個客戶端都可以由獨立的sessionID,與請求者一一對應
    3. 保存:request.session['KEY']=Value
    4. 獲取:request.session.get('KEY','默認值')或request.session['KEY']
    5. 刪除:del request.session['KEY']
  3. settings配置
    1. SESSION_COOKIE_AGE
    2. SESSION_EXPIRE_AT_BROWSER_CLOSE = True
  4. session存儲在數據庫中的django_session表
    1. session_key就是session_id
    2. 定期執行python manage.py clearsessions刪除已過期的session數據

緩存

一類可以更快的讀取數據的介質,加快數據訪問的存儲方式

意義:視圖渲染有一定成本,數據庫的頻繁查詢過高;所以對於低頻變動的頁面可以考慮使用緩存技術,減少渲染次數;用戶拿到響應時間的成本降低

場景特點:緩存的地方,數據變動頻率較少

整體緩存策略

  1. settings配置

    # 數據庫緩存
    CACHES={
    	'default':{
    		'BACKEND':'django.core.cache.backends.db.DatabaseCache',	# 也可以使用Redis
    		'LOCATION':'my_cache_table',
    		'TIMEOUT':300,	#緩存保存時間
    		'OPTIONS':{
    			'MAX_ENTRIES':300,	#緩存最大數據條數
    			'CULL_FREQUENCY':2,	#緩存條達到最大值時,刪除1/x的緩存數據
    		}
    	}
    }
    
  2. python manage.py createcachetable	# 創建緩存的表為my_cache_table
    
  3. 使用

    from django.views.decorators.cache import cache_page
    @cache_page(30)
    def my_view(request):
        ...
    如果緩存有,則走緩存不走視圖
    或者
    from django.views.decorators.cache import cache_page
    urlpatterns = [
        path('foo/',cache_page(60)(my_view))
    ]
    

局部緩存

  1. 方式一

    from django.core.cache import caches
    cache1 = caches['key']
    cache2 = caches['key2']
    
  2. 方式二

    from django.core.cache import cache
    # 相當於直接引入settings中CACHES配置項中的default項
    
  3. 緩存API的使用

    1. cache.set(key,value,timeout),cache.set_many(dict,timeout)
    2. cache.get(key),cache.get_many(key_list)
    3. cache.add(key,value)
    4. cache.get_or_set(key,value,timeout)
    5. cache.delete(key),cache.delete_many(key_list)
  4. 瀏覽器緩存

    1. 強緩存
      1. 響應頭
        1. Expires
        2. Cache-Control
    2. 協商緩存:針對靜態文件
      1. Last-Modified 和 If-Modified-Since:(不精准但省cpu)
        1. 最近修改時間,瀏覽器第一次請求靜態文件時,服務器如果返回Last_Modified響應頭,則需協商緩存
        2. 當緩存到期后,瀏覽器將獲取到的Lasted-Modified值作為請求頭If-Modified-Scine的值,與服務器發請求協商,服務器返回304,代表緩存繼續使用,200則緩存不可用
      2. Etag 和 If-None-Match:(精准但非cpu,因為MD5計算)
        1. 服務器器響應請求時,返回當前資源文件的一個唯一標識,只要資源有變化,Etag就會重新生成
        2. 當緩存到期后,瀏覽器將獲取到的Etag值作為請求頭If-None-Match的值,與服務器發請求協商,服務器返回304,代表緩存繼續使用,200則緩存不可用

中間件

請求/響應處理的鈎子框架、插件系統、全局改變IO(請求/響應)

以類的形式體現

  1. 編寫中間件

    1. 繼承django.utils.deprecation.MiddlewareMixin

    2. 實現下列5個方法的一個或多個

      1. process_request(self,request):執行路由之前被調用,在每個請求上被調用,返回None或HttpResponse對象
      2. process_view(self,request,callback,callback_args,callback_kwargs):調用視圖之前被調用,在每個請求上被調用,返回None或HttpResponse對象
      3. process_response(self,request,response):所有響應返回瀏覽器之前被調用,在每個響應上調用,返回HttpResponse對象
      4. process_exception(self,request,response):當處理過程中拋出異常之前時調用,返回一個HttpResponse對象
      5. process_template_response(self,request,response):在視圖函數執行完畢且試圖返回的對象中包含render方法;該方法需要返回實現了render方法的響應請求
      6. 中間件中大多數方法在返回None時表示忽略當前操作進行下一項事件,當返回HttpResponse對象時表示請求結束,直接返回給客戶端
    3. settings注冊中間件

      MIDDLEWARE=[
          ...
      ]
      # 請求是按數組順序執行,響應是按數組逆序執行
      
    4. 單獨創建一個文件夾存放中間件

    5. 場景例子:

      1. 3分鍾內同一IP客戶端只能訪問服務器5次
    6. request.META['REMOTE_ADDR']可以得到遠程客戶端的IP地址

    7. request.path_info可以得到客戶端訪問的請求路由信息

  2. CSRF-跨站偽造請求攻擊

    1. settings配置:確認MIDDLEWARE中django.middleware.csrf.CsrfMiddleWare是打開的,調試的時候可以關閉
    2. 模板中,form標簽下添加

項目部署

  1. uwsgi

    1. 項目遷移
    sudo scp project_path user@ip:deirection_path
    輸入root密碼
    
    1. 要點

      1. 用uWSGI替代python manage.py runserver方法啟動服務器
      2. 配置nginx反向代理服務器,接收用戶請求轉發給DJango
      3. 用nginx配置靜態文件路徑,解決靜態文件路徑問題
    2. uWSGI

      1. django原本是不識別http的,是通過runserver將http翻譯成WSGI規則后再訪問指定路由
      2. uWSGI是服務,是WSGI的一種,uwsgi是協議
    3. 安裝

      pip install uwsgi
      pip freeze | grep -i 'uwsgi'		檢查是否安裝
      
    4. 配置

      1. 添加配置文件uwsgi.ini到settings.py文件夾路徑下

        [uwsgi]
        socket = IP:port	# 此模式需要有nginx
        http = IP:port		# 有nginx的話這個就屏蔽掉
        chdir = project_path	# 絕對地址
        wsgi-file = project_name/wsgi.py	# 相對地址		
        process = 4		# 進程個數,幾核CPU就幾個
        threads = 2		# 線程個數,需要測
        pidfile = uwsgi.pid	# 服務的進程id文件
        daemonize = uwsgi.log	# 是否后台啟動以及日志文件位置,常用來查看服務異常信息
        master = True	# 主進程管理模式
        
        
      2. settings.py配置

        1. Debug=False
        2. ALLOWED_HOSTS = [....]
      3. 啟動:uwsgi --ini uwsgi.ini

        1. 代碼有任何改動都需要重啟uwsgi
        2. 端口號被占用解決方案:sudo lsof -i:端口號,根據端口查出進程,殺掉進程,重啟服務
        3. 停止:uwsgi --stop uwsgi.pid
      4. 常見問題

        1. 啟動失敗:端口失敗
        2. 停止失敗:stop無法關閉uWSGI,一般都是手動殺掉
  2. Nginx:輕量級高性能Web服務器

    1. 原理:客戶端請求nginx,再由nginx將請求轉發uWSGI運行的django

    2. 具備轉發功能,負載均衡;一般一個而網站不止一個Django服務

    3. 安裝

      1. sudo apt install nginx
      2. 強行霸占80端口
    4. 配置

      1. 修改/etc/nginx/sites-enabled/default配置文件

        server{
        	...
        	location / {
        		#try_files $uri $uri/ = 404		# 該句一定要禁掉
        		uwsgi_pass:IP:port;		# 重定向IP和端口
        		include /etc/nginx/uwsgi_params;
        	}
        	location /static{
        		root static_path
        	}
        }
        
      2. 啟動停止

        1. sudo /etc/init.d/nginx start/stop/restart/status
        2. sudo service nginx start/stop/restart/status
      3. nginx -t 可以查看配置的語法有沒有問題

      4. 修改uwsgi啟動模式,把http改成socket

    5. 配錯思路

      1. 排查問題宗旨->看日志
      2. nginx日志位置(日志位置可以在/etc/nginx/nginx.conf中修改)
        1. var/log/nginx/error.log
        2. var/log/nginx/access.log
      3. uwsgi日志:項目同名目錄下,uwsgi.log
      4. 502:nginx沒問題,但是uwsgi沒啟動
      5. 404:
        1. 路由的確不在django配置
        2. nginx配置錯誤,未禁掉try_files
    6. 靜態文件配置步驟

      1. 創建新路徑-存放所有靜態文件,即在項目路徑下創建static文件夾,此文件夾可以方Vue編譯好的靜態文件
      2. setting.py中添加配置
        1. STATIC_ROOT=static_path
      3. python manage.py collectstatic(前后端分離的話,可能只有admin后台需要)
    7. 郵箱告警配置

      # settings.py
      DEBUG=False
      # 錯誤報告接收方
      ADMIN=[
          (用戶名,郵箱地址),
          (用戶名,郵箱地址)
      ]
      #錯誤報告發送方
      SERVER_EMAIL = '郵箱'
      
      1. 過濾敏感信息

        1. 局部變量
        from django.views.decorators.debug import sensitive_variables
        
        @sensitive_variables('user','pw','cc') # 必須在最頂部
        def process_info(user):
        	pw=user.passwd
        	cc = user.credit_card_number
        	name = user.name
        
        1. post數據

          from django.views.decorators.debug import sensitive_post_parameters
          
          @sensitive_post_parameters('password','username')
          def index(request):
              s = request.POST['username']+request.POST['password']
          
          
    8. 華為雲環境部署

      1. 找最低配就可以了(最核心就是要公網IP,重點是便宜)

      2. SSH工具-FinalShell

      3. apt更換國內源(了解一下就好了,都是復制粘貼就行)

        mv /etc/apt/sources.list/ /etc/apt/sorces.list.bak
        cd /etc/apt
        vim sources.list
        
        #輸入如下
        deb http://mirrors.aliyun.com/ubuntu bionic main restricted universe muliverse
        deb http://mirrors.aliyun.com/ubuntu bionic-security main restricted universe muliverse
        deb http://mirrors.aliyun.com/ubuntu bionic-updates main restricted universe muliverse
        deb http://mirrors.aliyun.com/ubuntu bionic-proposed main restricted universe muliverse
        deb http://mirrors.aliyun.com/ubuntu bionic-backports main restricted universe muliverse
        deb-src http://mirrors.aliyun.com/ubuntu bionic main restricted universe muliverse
        deb-src http://mirrors.aliyun.com/ubuntu bionic-security main restricted universe muliverse
        deb-src http://mirrors.aliyun.com/ubuntu bionic-updates main restricted universe muliverse
        deb-src http://mirrors.aliyun.com/ubuntu bionic-proposed main restricted universe muliverse
        deb-src http://mirrors.aliyun.com/ubuntu bionic-backports main restricted universe muliverse
        
        最后執行 apt-get update 
        
      4. 安裝nginx:sudo apt-get install nginx

      5. 安裝mysql:sudo apt-get install mysql-server

        1. 查看cat /etc/mysql/debian.cnf,根據文件中的用戶名和密碼進入mysql
        2. 修改root用戶密碼
          1. update mysql.user set authentication_string=password('123456'),plugin="mysql_native_password" where user='root' and host='localhost';
          2. 刷新權限:flush privileges;
      6. 安裝redis:sudo apt-get install redis

      7. sudo apt-get install python3-pip,切成國內源

      8. 第三方python庫

      9. 安裝mysqlclient

        1. sudo apt-get install defualt-libmysqlclient-dev
        2. pip install mysqlclient
      10. 前端部署

        1. 修改nginx啟動 用戶

          1. vim /etc/nginx/nginx.conf

            user root; 修改nginx啟動用戶
            
          2. 修改nginx 80的默認配置:vim /etc/nginx/sites-enabled/default

            root /root/項目目錄/templates; #指定全局靜態路徑地址
            
            location /static {
            	root /root/項目目錄/;
            }
            
            location / {	#路由設定
            	rewrite ^/(\w+)$
            }
            
      11. 到這里就會發現會docker很重要,直接做個鏡像獲取就好了

前后端分離

  1. 跨域資源共享(CORS
    1. 跨域請求
      1. 簡單請求(以下三個必須同時滿足)
        1. 請求方法:GET、HEAD、POST
        2. 請求頭僅包含:Accept、Accept-Language、Content-Language、Content-Type(僅支持application/x-www-form-unlencoded、multipart/form-data、text-plain)
        3. 流程:
          1. 請求頭中攜帶Origin,表明自己來自哪個域
          2. 如果請求頭中的Origin在服務器接收范圍內,則返回如下頭
            1. Access-Control-Allow-Origin(必選)
            2. Access-Control-Allow-Credentials
            3. Access-Control-Expose-Headers
      2. 預檢請求 (以上三個條件破一個就是預檢請求)
        1. OPTIONS請求發起(都必須)
          1. Origin
          2. Access-Control-Request-Method
          3. Access-Control-Request-Headres
        2. OPTIONS接收響應階段
          1. Access-Control-Allow-Origin
          2. Access-Control-Allow-Methods
          3. Access-Control-Allow-Headers
          4. Access-Control-Allow-Credentials(可選)
          5. Access-Control-Max-Age(可選)
        3. Content-Type僅支持如下三種:
          1. application/x-www-form-urlencoded
          2. multipart/form-data
          3. text/plain
    2. CORS配置
      1. pip install django-cors-headers
      2. settings.py
        1. INSTALLED_APPS中添加corsheaders
        2. MIDDLEWARE中添加corheaders.middleware.CorsMiddleware在django.middleware.common.CommonMiddleware上方
        3. CORS_ORIGIN_ALLOW_ALL,如果未True,白名單不啟用
        4. CORS_ORIGIN_WHITELIST = ["https://...."]
        5. CORS_ALLOW_METHODS=('DELETE','GET','OPTIONS','PATCH','POST','PUT')
        6. CORS_ALLOW_HEADERS=('accept-encoding','authorization','content-type','dnt','origin','user-agent','x-csrftoken','x-requested-with')

JWT

  1. base64

    import base64
    base64.b64encode()
    base64.b64decode()
    urlsafe_b64encode()
    urlsafe_b64decode()
    
  2. hmac

    import hmac
    h = hmac.new(key,str,digestmod='SHA256') #SHA256加密
    h.digest() #獲取結果
    
  3. JWT是一種基於JSON的開放標准,一般用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,分三個部分,以 . 分割

    1. header

      1. 格式為字典-元數據

        {'alg':'HS256','typ':'JWT'}
        # 該部分數據需要轉成json串並用base64轉碼
        
    2. payload

      1. 格式為字典

        # 公共聲明(內置關鍵字)
        {
            'exp':xxx, # token過期時間
            'iss':xxx, # Issuer,token的簽發者
            'iat':xxx, # Issued At,創建的時間戳
            'aud':xxx, # Audience,token簽發面向的群體
        }
        # 私有聲明(自定義關鍵字)
        {'username':'xxxx'}
        
    3. signature

      HS256(自定義的key,base64后的header+'.'+base64后的payload)
      
  4. 校驗規則

    1. 解析header,確認alg
    2. 簽名校驗
    3. 獲取payload自定義內容
  5. 生成JWT

    1. pip install pyjwt
    2. jwt.encode(payload,key,algorithm),返回token
    3. jwt.decode(token,key,issuer,audience),返回payload
  6. 場景

    1. 原則

      1. JWT簽發后,交由瀏覽器保存,可存到本地存儲
      2. 需要用戶登陸才能使用,前端ajax中需要將jwt傳至后端;可放在請求頭中發送
    2. 代碼

      import django.conf import settings
      import time
      def make_token(username,expire=2600*24)
      	key = settings.JWT_TOKEN_KEY
      	now_t = time.time()
      	payload_data = {"username":username,'exp:'now_t+expire}
          return jwt.encode(payload_data,key,algorithm='HS256')
      
      def login(self,request):
          ...
          # 當天第一次登陸生成token
          token = make_token(username)
          result={'code':200,'username':username,'data':{'token':token.decode()}}
          return JsonResponse(result)
      
      
      // 前端獲取token並保存到本地存儲
      $.ajax({
          processData:false,
          contentType:'application/json',
          dataType:'json',
          data:JSON.stingify(post_data),
          url:url,
          type:'post',
          success:function(result){
              if(result.code==200){
       			// 將token和username存儲到本地存儲
                  window.localStorage.setItem('dnblog_token',result.data.token)
                  window.localStorage.setItem('dnblog_user',result.username)
                  alert('登陸成功')
              }else{
                  alert(result.error)
              }
          }
      })
      
      // 登陸后需要校驗登陸狀態token
      var token = window.localStorage.getItem('dnblog_token')
      var username = window.localStorage.getItem('username')
      
      $.ajax(
      	{
              processData:false,
              contentType:false,
              url:url,
              type:'post',
              data:formatdata,
              beforeSend:function(request){
                  request.setRequestHeader("AUthorization",token)},
              success:function(arg){
                  ...
              }
          }
      )
      
  7. 裝飾器校驗登陸狀態

    1. django提供了method_decorator,可以將函數裝飾器轉換成方法裝飾器

      @method_decorator(logging_check) # logging_check是函數裝飾器
      
    2. 在utils中定義裝飾器方法

      # logging_dec.py	閉包
      import jwt
      from django.conf import settings
      from models import UserProfile
      def logging_check(func):
          def warp(self,request,*args,**kwargs):
              # 獲取token 
              token = request.META.get('HTTP_AUTHORIZATION')
              #校驗toekn
              if not token:
                  result = {'code':403,'error':'Please login'}
              # 校驗jwt
              try:
                  # 看拿到的token能否被解釋出key並於JWT_TOKEN_KEY比較
                  res = jwt.decode(token,settings.JWT_TOKEN_KEY)
              except Exception as e:
                  print('jwt decode error is %s '% e)
                  result = {'code':403,'error':'Please login'}
                 	return JsonResponse(result)
              
              # 獲取登陸用戶
              username = res['username']
              user = UserProfile.object.get(username=username)
              request.myuser = user
              return func(request,*args,**kwargs)
          return wrap
      
      # views.py
      import utils.logging_dec import logging_check
      from django.utils.decorators import method_decorator
      
      @method_decorator(logging_check)
      def users_view(self,request,username):
      	if request.method != 'POST':
              result = {'code':403,'error':'Please login'}
              return JsonResponse(result)
          user = request.myuser
          
          
          
      
      

Celery

處理大量消息的分布式系統,專注於實時處理的任務隊列,同時也支持任務調度

  1. 安裝:sudo pip install Celery

  2. 名詞

    1. broker消息傳輸的中間件,生產者一旦有消息發送,將發至broker;broker可以是【RabbitMQ、Redis】

    2. backend:用於存儲消息/任務結果,如果需要跟蹤和查詢任務狀態,則需要添加相關配置

    3. worker:工作者(消費者),消費/執行broker中消息/任務的進程

      image

  3. 使用

    1. 創建worker

      # tasks_result.py
      from celery import Celery
      # xxx是app名字,自定義的
      # 初始化連接
      app = Celery('xxx',broker='redis://:password@127.0.0.1:6379/1',backend='redis://:password@127.0.0.1:6379/2')
      # backend指定結果存儲位置
      
      #創建任務函數
      @app.task
      def task_test(a,b):
      	print("task is running...")
          return a+b
      
    2. 啟動worker(以文件tasks_result.py)

      celery -A tasks_result worker --loglevel=info
      
    3. 創建生產者(一般都在django服務中)

      # tasks_result.py文件的同級目錄下
      from tasks_result import task_test
      s = task_test.delay(10,100)	# delay()就是執行任務函數
      s.result	# 獲取任務執行的結果,得到100+10
      # 執行完畢后,查看worker日志
      
  4. django中使用celery

    1. 創建配置文件

      1. 項目同名目錄下創建celery.py,和settings.py平級,用於實例化

        from celery import Celery
        from django.conf import settings
        import os
        
        # 設置項目到環境變量
        os.environ.setdefault('DJANGO_SETTING_MODULE','項目名.settings')
        app = Celery('app_name')
        # 配置中間件的URL
        app.conf.update(BORKER_URL='redis://:password@127.0.0.1:6379/1')
        
        # 自動加載各注冊的應用下的worker函數
        app.autodiscover_tasks(settings.INSTALLED_APPS) 
        
    2. 應用下創建worker集中定義對應worker函數

    #tasks.py   
    from xxx.celery import app
       @app.task
       def xxxx(*args,**kwargs):
           
    
    1. 視圖函數充當生產者,推送具體worker函數
       # views.py
       def XXXView(View):
           ...
       	xxxx.delay()
    
  5. 項目目錄下啟動worker

       celery -A 項目名 worker -l info
       # 正式環境(后台啟動)
       nohup celery -A 項目名 worker -P gevent -c 1000 >celery.log 2>&1 &
       # gevent 指開啟協程,-c 1000是開啟1000個協程
       # 2>&1: 2代表錯誤輸出,1代表標准輸出,即錯誤輸出重定向給標准輸出
    
  6. 注意settings的debug要為false,不然celery會有內存泄漏

  7. celery 后台啟動一個或多個worker

    celery multi start worker_name -A 項目名 -l info
    # start、restrat、stop、stopwait(完成了當前任務再停止)
    
  8. 調用

    1. delay()
    2. apply_async(),可以指定調用時執行的參數,例如運行的時間,使用的隊列等
  9. 配置了結果后端才可以

  10. 原語

    1. group
    2. chain
    3. chords

第三方支付

  1. RSA

    1. 公鑰加密/私鑰解密

    2. 私鑰簽名/公鑰驗簽

    3. RSA生成公私鑰

      #在項目路徑下
      openssl
      OpenSSL>genrsa -out app_private_key.pem 2048	#私鑰,不要外泄
      OpenSSL>rsa -in app_private_key.pem -pubout -out app_public_key.pem		#公鑰,可以互換
      OpenSSL>exit
      
  2. 沙箱添加RSA公鑰(不是公鑰證書)

    1. 只復制中間部分,首行和尾行不復制
    2. 點擊保存
    3. 復制支付寶(第三方支付平台)公鑰到alipay_public_key.pem(自己創建)中,記得添加公鑰的首行和尾行(之前不復制的兩行)
    4. 確認是三把鑰匙,app的公鑰和私鑰,阿里的公鑰
  3. 安裝第三方SDK

    1. pip install python-alipay-sdk
  4. 觸發支付視圖

    import django.conf import settings
    from alipay import AliPay
    
    app_private_key_string = open(settings.ALIPAY_KEY_DIRS + 'app_private_key.pem').read()
    
    app_public_key_string = open(settings.ALIPAY_KEY_DIRS + 'app_public_key.pem').read()
    
    class XXXVIew(View):
        def get(self,request):
            return Response()
        
        def post(self,request):
            # 返回支付地址
            # 接收到商品id后,生成訂單 訂單狀態 待付款 已付款 付款失敗
            # 使用沙箱賬號測試,兩個瀏覽器去測
            order_id = '%s' % (int(time.time()))
            pay_url = self.get_trade_url(order_id,price)
            
            return JsonResponse({"pay_url":pay_url})
    
    class MyAliPay(View):
        def __init__(self,**kwargs):
            """
            初始化支付對象
            """
            super().__init__(**kwargs)
            self.alipay = AliPay(
            	appid=settings.ALIPAY_APPID,
                app_praivte_key_string=app_private_key_string,
       			alipay_public_key_string=alipay_public_key_string,
                app_notify_url=None,
                sign_type='RSA2',
                debug=True  # 默認Fasle,True是走沙箱環境
            )
        def get_trade_url(self,order_id,amount):
            """
            獲取交易鏈接
            """
            order_string=self.alipay.api_alipay_trade_page_pay(
            	subject=order_id,
            	out_trade_no = order_id,
            	total_amount=amount,
            	return_url = settings.ALIPAY_RETURN_URL
            	notify_url = settings.ALIPAY_NOTIFY_URL
             )
                return "https://openapi.alipaydev.com/gateway.do?"+order_string
        def get_verify_result(self,data,sign):
            """
            獲取簽名驗證結果
            """
            return self.alipay.verify(data,sign)
        
        def get_trade_result(self,odrer_id):
            # 主動查詢交易結果
            result = self.alipay.api_alipay_trade_query(order_id)
            if result.get('trade_status') == 'TRADE_SUCCESS':
                return True
            return False
    
    $.ajax({
        type:'post',
        url:'',
        data:''
        success:function(res){
            window.location.href(res.pat_url)	//重定向到支付寶交易鏈接
        }
    })
    
  5. settings配置

    ALIPAY_KEY_DIRS = os.path.join(BASE_DIR,'static/key_file')
    # 把公鑰和私鑰放到static路徑下
    ALIPAY_APPID = 'xxxx' #在支付寶平台上拿
    ALIPAY_RETURN_URL="http://127.0.0.1:8000/payment/result"	# 返回支付結果界面,得到訂單狀態
    ALIPAY_NOTIFY_URL="http://127.0.0.1:8000/payment/result"	# 通知支付結果
    
  6. 支付結果異步通知

    class ResultView(MyAliPay):
        def post(self,request):
            #notify_url 業務邏輯
            request_data={k:request.POST[k] for k in request.POST.keys()}	# 將獲取到的數據封裝成字典
            sign = request_data.pop('sign')
            is_verify = self.get_verify_result(request_data,sign)
            if is_verify:
                trade_status = request_data.get('trade_status')
                if trade_status == "TRADE_SUCCESS":
                    print("支付成功")
                return HttpResponse("success")
            else:
                return Response('請求失敗')
            
        def get(self,request):
            # return_url 業務邏輯
            order_id = request.GET['out_trade_no']
            # 查詢訂單表狀態,如果還是待付款,采取B方案,主導查詢支付寶訂單狀態
            result = self.get_trade_result(order_id)
            if result:
                return HttpResponse('---支付成功-主動查詢')
            else:
           		return HttpResponse('---支付成功-主動失敗')     
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM