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