zoukankan      html  css  js  c++  java
  • Flask Web开发从入门到放弃(一)

    • 第1章 章节一

    • 01 内容概要

    • 02 内容回顾

    • 03 路飞学城之加入购物车

    • 04 路飞学城之结算

    • 05 路飞学城之立即支付

    • 06 路飞学城之后续计划

    • 07 Flask框架简介和快速使用

    • 08 FLask框架之用户管理系统示例(一)

    • 09 Flask框架之用户管理系统示例(二)

    • 第2章 章节二

    • 01 内容概要

    • 02 内容回顾

    • 03 Flask框架之配置

    • 04 Flask框架之创建路由的两种方式

    • 05 Flask框架之反向生成URL

    • 06 Flask框架之自定义路由转换器

    • 07 Flask框架之app.route参数

    • 08 Flask框架之获取子域名的路由

    • 09 上述内容总结

    • 10 Flask框架之视图中添加装饰器

    • 11 Flask框架之CBV和FBV

    • 12 Flask框架之请求和响应相关

    • 13 Flask框架之模板引擎

    • 14 Flask框架之session使用和源码流程(一)

    • 15 Flask框架之session使用和源码流程(二)

    • 16 Flask框架之before_request和after_request(一)

    • 17 Flask框架之after_request(二)

    • 18 Flask框架字号常见装饰器

    • 19 上述内容总结

    • 20 Flask之闪现

    • 21 Flask中间件

    • 22 Flask框架之蓝图(一)

    • 23 Flask框架之蓝图(二)

    • 24 拓展知识:pipreqs

    • 25 拓展知识:函数和方法

    • 第3章 章节三

    • 01 内容概要

    • 02 内容回顾

    • 03 threading.local学习

    • 04 自定义Local思路

    • 05 自定义Local对象(基于函数)

    • 06 自定义Local对象(基于面向对象)

    • 07 Flask上下文管理之本质分析

    • 08 Flask上下文管理之请求到处理阶段

    • 09 Flask上下文管理之视图调用阶段

    • 10 Flask上下文管理之视图调动阶段和结束阶段

    • 11 问题来了

    • 12 Flask中的g到底是什么呢?生命周期

    • 13 内容补充:面向对象的私有字段

    • 14 homework

    • 第4章 章节四

    • 01 内容概要

    • 02 内容回顾

    • 03 路飞学城补充:视频播放授权

    • 04 flask-session组件使用和原理(一)

    • 05 flask-session组件使用和原理(二)

    • 06 flask-session组件使用和原理(三)

    • 07 flask-session组件使用和原理(四)

    • 08 基于pymysql实现用户登录

    • 09 基于数据库连接池实现用户登录

    • 10 解决bug(一)

    • 11 解决bug(二)

    • 12 数据库连接池总结

    • 13 WTforms介绍以及用户登录示例

    • 14 WTforms用户注册示例

    • 15 内容总结

    • 16 homework

    第1章 章节一

    01 内容概要

    1.1 路飞购买流程

    • 加入购物车
    • 结算
    • 去支付

    1.2 Flask框架

    • 路由
    • 视图
    • 模板
    • session
    • ...

    02 内容回顾

    2.1 路飞学城项目结构

      2.1.1 前后端分离;

      2.1.2 导师后台+管理后台+主站(本人负责)

    2.2 主站的功能

      2.2.1 Vue——兼职、课程列表、详细、深科技

      2.2.2 rest api

    • 课程系列——列表、详细、推荐课程、章节&课时、常见问题、评论
    • 深科技——文章列表、详细、评论、赞、收藏
    • 个人中心——我的账户、个人资料、订单、课程中心
    • 购买流程(复杂)——加入购物车、去结算、立即支付
    • 其他——关于我们、联系我们、意见反馈

    2.3 技术点

      2.3.1 rest framework框架——认证组件(用于用户认证) or Django中间件,两者实现的时机不同,认证逻辑无需实现;

      2.3.2 跨域——jsonp(动态生成script标签) cors;

      2.3.3 Redis——购物逻辑,用户session两个场景下使用;

    • 频繁操作;
    • 中间状态;
    • 数据放入内容,速度快;
    • Redis锁

      2.3.4 支付宝支付接口

    • RSA加密;
    • 数字金额有要求,保留小数点后两位;
    • 两个URL;
    • 支付宝公钥和商户私钥;

      2.3.5 微信消息推送

    • 微信企业号
    • 沙箱环境
    • 普通消息和模板消息
    • 关注公众号,生成二维码
    • 通过js生成二维码
    • 唯一标识获取到

      2.3.6 ContenType

    • 参考Django settings
    • 参考Django 中间件

      2.3.7 rest framework分页

      2.3.8 接口写的最多的查询接口;

      2.3.9 视图

    • queryset

      2.3.10 序列化

    • __new__方法;
    • source
    • Method

      2.3.11 Git协同开发

      2.3.12 ORM操作

    • only
    • defer
    • exclude
    • filter

      2.3.13 CRSF

    • 基于中间件做
    • 基于装饰器

      2.3.14 Vue.js基本命令

    • Vue.js的基本命令
    • Router拦截器
    • Ajax——jQuery、axios ——本质都是XMLHttpRequest对象实现;
    • 请求头Content-Type:request.POST
    • json
    • vuex
    • vue-cookies

      2.3.15 面试题总结准备

      2.3.16 组织架构、人员配比、项目周期;

    03 路飞学城之加入购物车

    3.1 加入购物车 

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import json
    from django.core.exceptions import ObjectDoesNotExist
    from django.conf import settings
    
    from rest_framework.views import APIView
    from rest_framework.viewsets import ViewSetMixin
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.response import Response
    
    from repository import models
    
    from api.serializer.payment import ShoppingCarSerializer
    from api.utils.auth.token_auth import LuffyTokenAuthentication
    from api.utils.auth.token_permission import LuffyPermission
    from api.utils import redis_pool
    from api.utils.exception import PricePolicyDoesNotExist
    
    
    class ShoppingCarView(ViewSetMixin, APIView):
        """
        购物车接口
        """
        authentication_classes = [LuffyTokenAuthentication, ]
        permission_classes = [LuffyPermission, ]
    
        def get(self, request, *args, **kwargs):
            """
            根据用户ID获取购物车所有东西
            :param request: 
            :param args: 
            :param kwargs: 
            :return: 
            """
            response = {'code': 1000, 'data': None}
            try:
                product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
                if product_dict:
                    product_dict = json.loads(product_dict.decode('utf-8'))
                    response['data'] = product_dict
            except Exception as e:
                response['code'] = 1001
                response['msg'] = "获取购物车列表失败"
    
            return Response(response)
    
        def post(self, request, *args, **kwargs):
            """
            # 根据课程ID获取课程信息以及相关所有价格策略
            chopping_car = {
                request.user.id:{
                    course.id:{
                            title:'xx',
                            img:'xx',
                            choice_policy_id:1,
                            price_policy_dict:{
                                {id:1,price:'9.9', period:'1个月'},
                                {id:2,price:'19.9',period:'3个月'},
                                {id:3,price:'59.9',period:'8个月'},
                            },
                        }
                    },
                    course.id:[
                            title:'xx',
                            img:'xx',
                            choice_policy_id:1,
                            price_policy_dict:{
                                {id:1,price:'9.9', period:'1个月'},
                                {id:2,price:'19.9',period:'3个月'},
                                {id:3,price:'59.9',period:'8个月'},
                            },
                        ]
                    }
                }
            }
            :param request: 
            :param args: 
            :param kwargs: 
            :return: 
            """
    
            response = {'code': 1000, 'msg': None}
            try:
                course_id = int(request.data.get('course_id'))
                policy_id = int(request.data.get('policy_id'))
    
                # 获取课程信息
                course = models.Course.objects.exclude(course_type=2).filter(status=0).get(id=course_id)
    
                # 序列化课程信息,并获取其关联的所有价格策略
                ser = ShoppingCarSerializer(instance=course, many=False)
                product = ser.data
    
                # 判断价格策略是否存在
                policy_exist = False
                for policy in product['price_policy_list']:
                    if policy['id'] == policy_id:
                        policy_exist = True
                        break
                if not policy_exist:
                    raise PricePolicyDoesNotExist()
    
                # 设置默认选中的价格策略
                product.setdefault('choice_policy_id', policy_id)
                # 获取当前用户在购物车中已存在的课程,如果存在则更新,否则添加新课程
                product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
                if not product_dict:
                    product_dict = {course_id: product}
                else:
                    product_dict = json.loads(product_dict.decode('utf-8'))
                    product_dict[course_id] = product
                # 将新课程写入到购物车
                redis_pool.conn.hset(settings.REDIS_SHOPPING_CAR_KEY, request.user.id, json.dumps(product_dict))
    
            except ObjectDoesNotExist as e:
                response['code'] = 1001
                response['msg'] = '视频不存在'
            except PricePolicyDoesNotExist as e:
                response['code'] = 1002
                response['msg'] = '价格策略不存在'
            except Exception as e:
                print(e)
                response['code'] = 1003
                response['msg'] = '添加购物车失败'
    
            return Response(response)
    
        def delete(self, request, *args, **kwargs):
            """
            删除购物车中的课程
            :param request: 
            :param args: 
            :param kwargs: 
            :return: 
            """
            response = {'code': 1000}
            try:
                course_id = kwargs.get('pk')
                product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
                if not product_dict:
                    raise Exception('购物车中无课程')
                product_dict = json.loads(product_dict.decode('utf-8'))
                if course_id not in product_dict:
                    raise Exception('购物车中无该商品')
                del product_dict[course_id]
                redis_pool.conn.hset(settings.REDIS_SHOPPING_CAR_KEY, request.user.id, json.dumps(product_dict))
            except Exception as e:
                response['code'] = 1001
                response['msg'] = str(e)
    
            return Response(response)
    
        def put(self, request, *args, **kwargs):
            """
            更新购物车中的课程的默认的价格策略
            :param request: 
            :param args: 
            :param kwargs: 
            :return: 
            """
            response = {'code': 1000}
            try:
                course_id = kwargs.get('pk')
                policy_id = request.data.get('policy_id')
                product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
                if not product_dict:
                    raise Exception('购物车清单不存在')
                product_dict = json.loads(product_dict.decode('utf-8'))
                if course_id not in product_dict:
                    raise Exception('购物车清单中商品不存在')
    
                policy_exist = False
                for policy in product_dict[course_id]['price_policy_list']:
                    if policy['id'] == policy_id:
                        policy_exist = True
                        break
                if not policy_exist:
                    raise PricePolicyDoesNotExist()
    
                product_dict[course_id]['choice_policy_id'] = policy_id
                redis_pool.conn.hset(settings.REDIS_SHOPPING_CAR_KEY, request.user.id, json.dumps(product_dict))
            except PricePolicyDoesNotExist as e:
                response['code'] = 1001
                response['msg'] = '价格策略不存在'
            except Exception as e:
                response['code'] = 1002
                response['msg'] = str(e)
    
            return Response(response)

    04 路飞学城之结算

    4.1 结算 

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import json
    import datetime
    from django.conf import settings
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from api.utils.auth.token_auth import LuffyTokenAuthentication
    from api.utils.auth.token_permission import LuffyPermission
    from api.utils import redis_pool
    from repository import models
    
    
    class PaymentView(APIView):
        """
        去结算接口
        """
        authentication_classes = [LuffyTokenAuthentication, ]
        permission_classes = [LuffyPermission, ]
    
        def get(self, request, *args, **kwargs):
            """
            获取结算列表
            :param request: 
            :param args: 
            :param kwargs: 
            :return: 
            """
            response = {'code': 1000}
            try:
                # 结算商品列表
                payment_list = redis_pool.conn.hget(settings.REDIS_PAYMENT_KEY, request.user.id)
                if not payment_list:
                    raise Exception()
    
                response['data'] = {
                    'payment_list': json.loads(payment_list.decode('utf-8')),  # 结算信息(课程、价格和优惠券)
                    "balance": request.user.balance  # 个人贝里账户,可使用贝里金额
                }
            except Exception as e:
                response['code'] = 1001
                response['msg'] = "结算列表为空"
    
            return Response(response)
    
        def post(self, request, *args, **kwargs):
            """
            去结算
                方案一(示例):用户提交课程id,去redis购物车中获取其选好的价格策略,再次检测课程和价格策略的合法性。
                       PS: 直接购买时,需要先加入购物车,再立即去结算
    
                方案二:用户提交课程id和价格策略id,去数据库验证其合法性。
                       PS: 直接购买时,直接去结算
                
                user.id: {
                    policy_course_dict:{
                        课程ID:{
                            'course_id': course_id,
                            'course_name': product['name'],
                            'course_img': product['course_img'],
                            'policy_id': product['choice_policy_id'],
                            'policy_price': policy_price,
                            'policy_': policy_period,
                            'coupon_record_list': [
                                {'id': 0, 'text': '请选择优惠券'},
                                {'id': 1, 'type':1, 'text': '优惠券1', ..},
                                {'id': 2, 'type':2, 'text': '优惠券1', ..},
                                {'id': 3, 'type':3, 'text': '优惠券1', ..},
                            ],
                        },
                        课程ID:{
                            'course_id': course_id,
                            'course_name': product['name'],
                            'course_img': product['course_img'],
                            'policy_id': product['choice_policy_id'],
                            'policy_price': policy_price,
                            'policy_': policy_period,
                            'coupon_record_list': [
                                {'id': 0, 'text': '请选择优惠券'},
                                {'id': 1, 'type':1, 'text': '优惠券1', ..},
                                {'id': 2, 'type':2, 'text': '优惠券1', ..},
                                {'id': 3, 'type':3, 'text': '优惠券1', ..},
                            ],
                        }
                    },
                    global_coupon_dict:{
                        1:{'type': 0, 'text': "通用优惠券", 'id': 1, ..},
                        2:{'type': 0, 'text': "通用优惠券", 'id': 2, ..},
                        3:{'type': 0, 'text': "通用优惠券", 'id': 3, ...},
                        4:{'type': 0, 'text': "通用优惠券", 'id': 4, ...},
                    }
                }  
                       
                
            :param request: 
            :param args: 
            :param kwargs: 
            :return: 
            """
            response = {'code': 1001}
            try:
    
                """
                1. 获取要支付的课程ID
                2. 检查购物车中是否存在,不存在则报错
                    循环用户提交的课程ID,去购物车中获取,如果不存在,就报错。
                    
                """
                # 获取用户提交的课程id
                course_id_list = request.data.get('course_list')
                if not course_id_list or not isinstance(course_id_list, list):
                    raise Exception('请选择要结算的课程')
    
                # 购物车中检查是否已经有课程(应该有课程的)
                product_dict = redis_pool.conn.hget(settings.REDIS_SHOPPING_CAR_KEY, request.user.id)
                if not product_dict:
                    raise Exception('购物车无课程')
    
                # 购物车中是否有用户要购买的课程
                product_dict = json.loads(product_dict.decode('utf-8'))
    
                # ###### 课程、价格和优惠券 #######
                policy_course_dict = {}
    
                for course_id in course_id_list:
                    course_id = str(course_id)
                    product = product_dict.get(course_id)
                    if not product:
                        raise Exception('购买的课程必须先加入购物车')
    
                    policy_exist = False
                    for policy in product['price_policy_list']:
                        if policy['id'] == product['choice_policy_id']:
                            policy_price = policy['price']
                            policy_period = policy['period']
                            policy_valid_period = policy['valid_period']
                            policy_exist = True
                            break
                    if not policy_exist:
                        raise Exception('购物车中的课程无此价格')
    
                    policy_course = {
                        'course_id': course_id,
                        'course_name': product['name'],
                        'course_img': product['course_img'],
                        'policy_id': product['choice_policy_id'],
                        'policy_price': policy_price,
                        'policy_period': policy_period,
                        'policy_valid_period': policy_valid_period,
                        'coupon_record_list': [
                            {'id': 0, 'text': '请选择优惠券'},
                        ],
                    }
                    policy_course_dict[course_id] = policy_course
    
                # 获取当前所有优惠券
                user_coupon_list = models.CouponRecord.objects.filter(account=request.user,
                                                                      status=0)
                # ###### 全局优惠券 #######
                global_coupon_record_dict = {}
    
                # 课程优惠券添加到课程中;全局优惠券添加到全局
                current_date = datetime.datetime.now().date()
                for record in user_coupon_list:
                    # 检查优惠券是否已经过期
                    begin_date = record.coupon.valid_begin_date
                    end_date = record.coupon.valid_end_date
                    if begin_date:
                        if current_date < begin_date:
                            continue
                    if end_date:
                        if current_date > end_date:
                            continue
                    # 全局优惠券
                    if not record.coupon.content_type:
                        if record.coupon.coupon_type == 0:
                            temp = {'type': 0, 'text': "通用优惠券", 'id': record.id,
                                    'begin_date': begin_date, 'end_date': end_date,
                                    'money_equivalent_value': record.coupon.money_equivalent_value}
                        elif record.coupon.coupon_type == 1:
                            temp = {'type': 1, 'text': "满减券", 'id': record.id,
                                    'begin_date': begin_date, 'end_date': end_date,
                                    'minimum_consume': record.coupon.minimum_consume,
                                    'money_equivalent_value': record.coupon.money_equivalent_value}
                        elif record.coupon.coupon_type == 2:
                            temp = {'type': 2, 'text': "折扣券", 'id': record.id,
                                    'begin_date': begin_date, 'end_date': end_date,
                                    'off_percent': record.coupon.off_percent}
                        else:
                            continue
    
                        global_coupon_record_dict[record.id] = temp
                    # 课程优惠券
                    else:
                        cid = record.coupon.object_id
                        if record.coupon.content_type.model == 'course' and cid in policy_course_dict:
                            # 课程价格:满减,打折,通用
                            if record.coupon.coupon_type == 0:
                                temp = {'type': 0, 'text': "通用优惠券", 'id': record.id,
                                        'begin_date': begin_date, 'end_date': end_date,
                                        'money_equivalent_value': record.coupon.money_equivalent_value}
                            elif record.coupon.coupon_type == 1 and policy_course_dict[cid][
                                'policy_price'] >= record.coupon.minimum_consume:
                                temp = {'type': 1, 'text': "满减券", 'id': record.id,
                                        'begin_date': begin_date, 'end_date': end_date,
                                        'minimum_consume': record.coupon.minimum_consume,
                                        'money_equivalent_value': record.coupon.money_equivalent_value}
                            elif record.coupon.coupon_type == 2:
                                temp = {'type': 2, 'text': "折扣券", 'id': record.id,
                                        'begin_date': begin_date, 'end_date': end_date,
                                        'off_percent': record.coupon.off_percent}
                            else:
                                continue
                            policy_course_dict[cid]['coupon_record_list'].append(temp)
    
                user_pay = {
                    'policy_course_dict': policy_course_dict,
                    'global_coupon_record_dict': global_coupon_record_dict
                }
                redis_pool.conn.hset(settings.REDIS_PAYMENT_KEY, request.user.id, json.dumps(user_pay))
    
            except Exception as e:
                response['code'] = 1002
                response['msg'] = str(e)
    
            return Response(response)

    05 路飞学城之立即支付

    5.1 立即支付 

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import json
    import time
    import random
    import datetime
    from django.conf import settings
    from django.db import transaction
    from django.db.models import F
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from api.utils.auth.token_auth import LuffyTokenAuthentication
    from api.utils.auth.token_permission import LuffyPermission
    from api.utils import redis_pool
    from api.utils.alipay import AliPay
    
    from repository import models
    
    
    def generate_order_num():
        """
        生成订单编号, 且必须唯一
        :return:
        """
        while True:
            order_num = time.strftime('%Y%m%d%H%M%S', time.localtime()) + str(random.randint(111, 999))
            if not models.Order.objects.filter(order_number=order_num).exists():
                break
        return order_num
    
    
    def generate_transaction_num():
        """
        生成流水编号, 且必须唯一
        :return:
        """
        while True:
            transaction_number = time.strftime('%Y%m%d%H%M%S', time.localtime()) + str(random.randint(111, 999))
            if not models.TransactionRecord.objects.filter(transaction_number=transaction_number).exists():
                break
        return transaction_number
    
    
    class PayOrderView(APIView):
        authentication_classes = [LuffyTokenAuthentication, ]
        permission_classes = [LuffyPermission, ]
    
        def post(self, request, *args, **kwargs):
            """
            去支付,生成订单。
            获取前端提交的购买信息
                {
                    course_price_list:[
                        {'policy_id':1, '':'course_id':1, 'coupon_record_id':1},
                        {'policy_id':2, '':'course_id':2, 'coupon_record_id':2},
                    ],
                    coupon_record_id:1,
                    alipay: 99,
                    balance: 1
                }
    
            1. 用户提交
                - balance
                - alipay
            2. 获取去结算列表
    
            课程
            3. 循环所有课程
                - 获取原价
                - 抵扣的钱
    
    
    
    
            :param request: 
            :param args: 
            :param kwargs: 
            :return: 
            """
            response = {'code': 1000}
            try:
                # 用户请求验证
                policy_course_list = request.data.get('course_price_list')
                coupon_record_id = request.data.get('coupon_record_id')
                alipay = request.data.get('alipay')  # >= 0
                balance = request.data.get('balance')  # >= 0
    
                if balance > request.user.balance:
                    raise Exception('账户中贝里余额不足')
    
                # 检查用户提交的信息在 redis结算列表 中是否存在,如果不存在,则需要用户从购物车中再次去结算
                payment_dict_bytes = redis_pool.conn.hget(settings.REDIS_PAYMENT_KEY, request.user.id)
                payment_dict = json.loads(payment_dict_bytes.decode('utf-8'))
    
                policy_course_dict = payment_dict['policy_course_dict']
                global_coupon_record_dict = payment_dict['global_coupon_record_dict']
    
                global_coupon_record = {}
                # 全局优惠券
                if coupon_record_id:
                    if  coupon_record_id not in global_coupon_record_dict:
                        raise Exception('全局优惠券在缓存中不存在')
                    global_coupon_record = global_coupon_record_dict[coupon_record_id]
    
                # 当前时间
                current_date = datetime.datetime.now().date()
                current_datetime = datetime.datetime.now()
    
                # 原价
                total_price = 0
                # 总抵扣的钱
                discount = 0
                # 使用优惠券ID列表
                if coupon_record_id:
                    use_coupon_record_id_list = [coupon_record_id, ]
                else:
                    use_coupon_record_id_list=[]
                # 课程和优惠券
                buy_course_record = []
    
                for cp in policy_course_list:
                    _policy_id = cp['policy_id']
                    _course_id = cp['course_id']
                    _coupon_record_id = cp['coupon_record_id']
    
                    temp = {
                        'course_id': _course_id,
                        'course_name': "course",
                        'valid_period': 0,  # 有效期:30
                        'period': 0,  # 有效期:一个月
                        'original_price': 0,
                        'price': 0,
                    }
    
    
                    if str(_course_id) not in policy_course_dict:
                        raise Exception('课程在缓存中不存在')
    
                    redis_course = policy_course_dict[str(_course_id)]
    
                    if str(_policy_id) != str(redis_course['policy_id']):
                        raise Exception('价格策略在缓存中不存在')
    
                    # 课程是否已经下线或价格策略被修改
                    policy_object = models.PricePolicy.objects.get(id=_policy_id)  # 价格策略对象
                    course_object = policy_object.content_object  # 课程对象
    
                    if course_object.id != _course_id:
                        raise Exception('课程和价格策略对应失败')
                    if course_object.status != 0:
                        raise Exception('课程已下线,无法购买')
    
                    # 选择的优惠券是否在缓存中
                    redis_coupon_list = redis_course['coupon_record_list']
                    redis_coupon_record = None
                    for item in redis_coupon_list:
                        if item['id'] == _coupon_record_id:
                            redis_coupon_record = item
                            break
                    if not redis_coupon_record:
                        raise Exception('单课程优惠券在缓存中不存在')
    
                    # 计算购买原总价
                    total_price += policy_object.price
    
                    # 未使用单课程优惠券
                    if redis_coupon_record['id'] == 0:
                        temp['price'] = policy_object.price
                        buy_course_record.append(temp)
                        continue
    
                    temp['original_price'] = policy_object.price
                    temp['valid_period'] = redis_coupon_record['policy_valid_period']
                    temp['period'] = redis_coupon_record['policy_period']
    
                    # 缓存中的优惠券是否已经过期
                    begin_date = redis_coupon_record.get('begin_date')
                    end_date = redis_coupon_record.get('end_date')
                    if begin_date:
                        if current_date < begin_date:
                            raise Exception('优惠券使用还未到时间')
                    if end_date:
                        if current_date > end_date:
                            raise Exception('优惠券已过期')
    
                    # 使用的是单课程优惠券抵扣了多少钱;使用的 个人优惠券ID
                    if redis_coupon_record['type'] == 0:
                        # 通用优惠券
                        money = redis_coupon_record['money_equivalent_value']
                        discount += money
                    elif redis_coupon_record['type'] == 1:
                        # 满减券
                        money = redis_coupon_record['money_equivalent_value']
                        minimum_consume = redis_coupon_record['minimum_consume']
                        if policy_object.price >= minimum_consume:
                            discount += money
                    elif redis_coupon_record['type'] == 2:
                        # 打折券
                        money = policy_object.price * redis_coupon_record['off_percent']
                        discount += money
    
                    temp['price'] = policy_object.price - money
                    buy_course_record.append(temp)
                    use_coupon_record_id_list.append(redis_coupon_record['id'])
    
                # 全局优惠券
                print(global_coupon_record)
                begin_date = global_coupon_record.get('begin_date')
                end_date = global_coupon_record.get('end_date')
                if begin_date:
                    if current_date < begin_date:
                        raise Exception('优惠券使用还未到时间')
                if end_date:
                    if current_date > end_date:
                        raise Exception('优惠券已过期')
    
                # 使用全局优惠券抵扣了多少钱
                if global_coupon_record.get('type') == 0:
                    # 通用优惠券
                    money = global_coupon_record['money_equivalent_value']
                    discount += money
                elif global_coupon_record.get('type') == 1:
                    # 满减券
                    money = global_coupon_record['money_equivalent_value']
                    minimum_consume = global_coupon_record['minimum_consume']
                    if (total_price - discount) >= minimum_consume:
                        discount += money
                elif global_coupon_record.get('type') == 2:
                    # 打折券
                    money = (total_price - discount) * global_coupon_record['off_percent']
                    discount += money
    
                # 贝里抵扣的钱
                if balance:
                    discount += balance
    
                if (alipay + discount) != total_price:
                    raise Exception('总价、优惠券抵扣、贝里抵扣和实际支付的金额不符')
    
                # 创建订单 + 支付宝支付
                # 创建订单详细
                # 贝里抵扣 + 贝里记录
                # 优惠券状态更新
                actual_amount = 0
                if alipay:
                    payment_type = 1  # 支付宝
                    actual_amount = alipay
                elif balance:
                    payment_type = 3  # 贝里
                else:
                    payment_type = 2  # 优惠码
    
                with transaction.atomic():
                    order_num = generate_order_num()
                    if payment_type == 1:
                        order_object = models.Order.objects.create(
                            payment_type=payment_type,
                            order_number=order_num,
                            account=request.user,
                            actual_amount=actual_amount,
                            status=1,  # 待支付
                        )
                    else:
                        order_object = models.Order.objects.create(
                            payment_type=payment_type,
                            order_number=order_num,
                            account=request.user,
                            actual_amount=actual_amount,
                            status=0,  # 支付成功,优惠券和贝里已够支付
                            pay_time=current_datetime
                        )
    
                    for item in buy_course_record:
    
                        detail = models.OrderDetail.objects.create(
                            order=order_object,
                            content_object=models.Course.objects.get(id=item['course_id']),
                            original_price=item['original_price'],
                            price=item['price'],
                            valid_period_display=item['period'],
                            valid_period=item['valid_period']
                        )
                    models.Account.objects.filter(id=request.user.id).update(balance=F('balance') - balance)
                    models.TransactionRecord.objects.create(
                        account=request.user,
                        amount=request.user.balance,
                        balance=request.user.balance - balance,
                        transaction_type=1,
                        content_object=order_object,
                        transaction_number=generate_transaction_num()
                    )
                    effect_row = models.CouponRecord.objects.filter(id__in=use_coupon_record_id_list).update(
                        order=order_object,
                        used_time=current_datetime)
    
    
                    if effect_row != len(use_coupon_record_id_list):
                        raise Exception('优惠券使用失败')
    
                    response['payment_type'] = payment_type
                    # 生成支付宝URL地址
                    if payment_type == 1:
                        pay = AliPay(debug=True)
                        query_params = pay.direct_pay(
                            subject="路飞学城",  # 商品简单描述
                            out_trade_no=order_num,  # 商户订单号
                            total_amount=actual_amount,  # 交易金额(单位: 元 保留俩位小数)
                        )
                        pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)
    
                        response['pay_url'] = pay_url
    
            except IndentationError as e:
                response['code'] = 1001
                response['msg'] = str(e)
    
            return Response(response)

    06 路飞学城之后续计划

    6.1 后续计划

    07 Flask框架简介和快速使用

    7.1 Flask Web框架;

    7.1.1 Flask的上下文管理;

    7.1.2 谈谈你对Python相关的Web框架的理解;

    • Django:大而全,重武器,内部提供:ORM、Admin、中间件、Form、ModelForm、Session、缓存、信号、CSRF;
    • Flask:短小精悍,可拓展强,http://flask.pocoo.org/ 
    • Tornado,短小精悍,可拓展性较之Flask弱一些,但优点是:异步非阻塞;
    • Web.py:比较老的Web框架;
    • bottle.py:微小,1000行左右;
    • Django的请求过来先走wsgiref,然后middleware;
    • Flask中的WSGI是Werkzurg;

    7.2 如何证明Flask内部是Werkzeug

      7.2.1 Flask的路由是装饰器;

    # -*- coding:utf-8 -*-
    # Project: FlaskFull 
    # Software: PyCharm
    # Time    : 2018-09-17 10:12
    # File    : s2.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route('/index/')
    def index():
        return 'Hello World!'
    
    
    if __name__ == '__main__':
        app.run()#run_simple(host,port,app)

    08 FLask框架之用户管理系统示例(一)

    8.1 Flask框架之用户登录程序;

    8.2 使用Pycharm安装Flask;

    from flask import Flask, render_template, request, redirect, session
    
    app = Flask(__name__, template_folder='templates')
    # 基于app这个对象设置secret_key的值,任意设置!
    app.secret_key = 'nishifdalkj4389!@#$28908'
    
    
    @app.route('/login/', methods=['GET', 'POST'])
    def hello_world():
        # return 'Hello World!'
        if request.method == "GET":
            return render_template('login.html')
        user = request.form.get('usr')
        pwd = request.form.get('pwd')
        if user == 'cuixiaozhao' and pwd == '123456':
            # 将用户信息放入session;
            session['user_info'] = user
            """
            RuntimeError: The session is unavailable because no secret key was set.  Set the secret_key on the application to something unique and secret.
    127.0.0.1 - - [17/Sep/2018 21:02:04] "POST /login/ HTTP/1.1" 500 -
            """
            return redirect('/index/')
        else:
            # 两种传值方法均可,比Django灵活一些;
            # return render_template('login.html', msg='用户名或者密码错误!')
            return render_template('login.html', **{'msg': '用户名或者密码错误!'})
    
    
    @app.route('/index/')
    def index():
        user_info = session.get('user_info')
        if not user_info:
            return redirect('/login/')
        else:
            return '欢迎登陆!'
    
    
    @app.route('/logout/')
    def logout():
        del session['user_info']
        return redirect('/login/')
    
    
    if __name__ == '__main__':
        app.run()

    09 Flask框架之用户管理系统示例(二)

    9.1 Flask框架用户登录示例二; 

    from flask import Flask, render_template, request, redirect, session
    
    app = Flask(__name__, template_folder='templates')
    # 基于app这个对象设置secret_key的值,任意设置!
    app.secret_key = 'nishifdalkj4389!@#$28908'
    app.debug = True
    USER_DICT = {
        '1': {'name': '志军', 'age': 18},
        '2': {'name': '大伟', 'age': 48},
        '3': {'name': '美凯', 'age': 38}
    }
    
    
    @app.route('/login/', methods=['GET', 'POST'])
    def hello_world():
        # return 'Hello World!'
        if request.method == "GET":
            return render_template('login.html')
        user = request.form.get('usr')
        pwd = request.form.get('pwd')
        if user == 'cuixiaozhao' and pwd == '123456':
            # 将用户信息放入session;
            session['user_info'] = user
            """
            RuntimeError: The session is unavailable because no secret key was set.  Set the secret_key on the application to something unique and secret.
    127.0.0.1 - - [17/Sep/2018 21:02:04] "POST /login/ HTTP/1.1" 500 -
            """
            return redirect('/index/')
        else:
            # 两种传值方法均可,比Django灵活一些;
            # return render_template('login.html', msg='用户名或者密码错误!')
            return render_template('login.html', **{'msg': '用户名或者密码错误!'})
    
    
    @app.route('/index/')
    def index():
        user_info = session.get('user_info')
        if not user_info:
            return redirect('/login/')
        return render_template('index.html', user_dict=USER_DICT)
    
    
    @app.route('/detail/')
    def detail():
        user_info = session.get('user_info')
        if not user_info:
            return redirect('/login/')
        uid = request.args.get('uid')
        info = USER_DICT.get(uid)
        return render_template('detail.html', info=info)
    
    
    @app.route('/logout/')
    def logout():
        del session['user_info']
        return redirect('/login/')
    
    
    if __name__ == '__main__':
        app.run()

    10 homework

    10.1 Flask装饰器、位置、url起个别名;

    10.2 类似于Django中间件的东西,before_request装饰器;

    10.3 上下文管理预习;

    • threading.local;

    • functools.wrappers;

    • functools.partial;

    • 面向对象中——__setarrt__;__getattr__;__delatrr__;

    第2章 章节二

    01 内容概要

    1.1 配置文件;

    1.2 路由

    1.3 视图函数

    1.4 请求和响应

    1.5 templates模板

    1.6 session(默认存储的签名的cookies中)

    1.7 flash闪现

    1.8 蓝图blueprint

    1.9 常见的装饰器before_request

    1.10 Flask中间件

    02 内容回顾

    2.1 装饰器;

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 10:45
    # File    : 1.装饰器.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    import functools
    def wapper(func):
        @functools.wraps(func)
        def inner(*args,**kwargs):
            return func(*args,**kwargs)
        return inner
    '''
    1、执行wapper函数,并将被装饰的函数当做参数。wapper(index)
    2、将第一步的返回值,重新赋值给index = wapper(old index)
    
    '''
    
    #1、为什么要使用装饰器?在不改变原来函数的基础之上,对函数执行前后进行自定义操作;
    @wapper
    def index(a1):
        return a1 +1000
    
    v = index(2)
    print(v)
    #获取函数名
    print("打印函数名:",index.__name__)
    
    
    @wapper
    def order(a1):
        return a1+1000
    
    print(index.__name__)
    print(order.__name__)

    2.2 带参数的装饰器;

    2.3 什么是面向对象,为什么要使用面向对象?

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 11:06
    # File    : 3.面向对象.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    ""
    """
    谈谈你对面向对象的认识?
    封装:
        将同一类方法分为一类,方法封装到类中;
        将方法中的共同的参数封装到对象中,把共同的值封装到对象中;
    """
    
    
    # 用户类实现;
    class File:
        def __init__(self, a1, a2, a3, a4):
            self.a1 = a1
            self.a2 = a2
            self.a3 = a3
            self.a4 = a4
    
        def file_add(self):
            pass
    
        def file_del(self):
            pass
    
        def file_update(self):
            pass
    
        def file_fetch(self):
            pass
    
    
    # 给了一些值,将数据加工,应用场景:Django自定义分页;
    class Foo():
        def __init__(self, a1, a2, a3, a4, a5, a6, a7):
            self.a1 = a1
            self.a2 = a2
            self.a3 = a3
            self.a4 = a4
            self.a5 = a5
            self.a6 = a6
            self.a7 = a7
    
        def sum(self):
            return self.a1 + self.a2
    
        def reduce(self):
            return self.a5 - self.a7
    
    
    obj = File(1, 2, 3, 4)
    print(obj)  # <__main__.File object at 0x10bbf25c0>
    
    
    class A(object):
        def __init__(self):
            self.age1 = 123
            self.a = A()
    
    
    class B(object):
        def __init__(self):
            self.age2 = 123
            self.b = B()
    
    
    class C(object):
        def __init__(self):
            self.age3 = 123
            self.c = C()
    
    
    class D(object):
        def __init__(self):
            self.age4 = 123
            self.d = D()

    03 Flask框架之配置

    3.1 app.py;

    from flask import Flask, render_template, redirect
    
    app = Flask(__name__)
    # Flask的配置文件这么玩耍;
    app.config.from_object("settings.DevelopmentConfig")#settings后面是一个类名;
    
    
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        return 'Hello World!'
    
    
    if __name__ == '__main__':
        app.run()

    3.2 指定settings.py文件; 

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 11:25
    # File    : settings.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    class BaseConfig(object):
        DEBUG = False
        TESTING = False
        DATABASE_URI = 'sqllite://:memory:'
    
    
    class ProductionConfig(BaseConfig):
        DATABASE_URI = 'mysql://user@production/foo'
    
    
    class DevelopmentConfig(BaseConfig):
        DEBUG = True
        DATABASE_URI = 'mysql://user@development/foo'
    
    
    class TestingConfig(BaseConfig):
        DEBUG = True
        DATABASE_URI = 'mysql://user@test/foo'

    3.3 Flask配置文件详解; 

    flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
        {
            'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式;
            'TESTING':                              False,                          是否开启测试模式;
            'PROPAGATE_EXCEPTIONS':                 None,                          
            'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
            'SECRET_KEY':                           None,
            'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
            'USE_X_SENDFILE':                       False,
            'LOGGER_NAME':                          None,
            'LOGGER_HANDLER_POLICY':               'always',
            'SERVER_NAME':                          None,
            'APPLICATION_ROOT':                     None,
            'SESSION_COOKIE_NAME':                  'session',
            'SESSION_COOKIE_DOMAIN':                None,
            'SESSION_COOKIE_PATH':                  None,
            'SESSION_COOKIE_HTTPONLY':              True,
            'SESSION_COOKIE_SECURE':                False,
            'SESSION_REFRESH_EACH_REQUEST':         True,
            'MAX_CONTENT_LENGTH':                   None,
            'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
            'TRAP_BAD_REQUEST_ERRORS':              False,
            'TRAP_HTTP_EXCEPTIONS':                 False,
            'EXPLAIN_TEMPLATE_LOADING':             False,
            'PREFERRED_URL_SCHEME':                 'http',
            'JSON_AS_ASCII':                        True,
            'JSON_SORT_KEYS':                       True,
            'JSONIFY_PRETTYPRINT_REGULAR':          True,
            'JSONIFY_MIMETYPE':                     'application/json',
            'TEMPLATES_AUTO_RELOAD':                None,
        }
     
    方式一:
        app.config['DEBUG'] = True
     
        PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
     
    方式二:
        app.config.from_pyfile("python文件名称")
            如:
                settings.py
                    DEBUG = True
     
                app.config.from_pyfile("settings.py")
     
        app.config.from_envvar("环境变量名称")
            环境变量的值为python文件名称名称,内部调用from_pyfile方法
     
     
        app.config.from_json("json文件名称")
            JSON文件名称,必须是json格式,因为内部会执行json.loads
     
        app.config.from_mapping({'DEBUG':True})
            字典格式
     
        app.config.from_object("python类或类的路径")
     
            app.config.from_object('pro_flask.settings.TestingConfig')
     
            settings.py
     
                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
     
            PS: 从sys.path中已经存在路径开始写;
         
     
        PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录;

    04 Flask框架之创建路由的两种方式

    4.1 基于@app.route('/index/', methods=['GET', 'POST'])装饰器方式实现;

    4.2 通过研究源代码,基于app.add_url_rule('/order/', view_func=order)实现;

    from flask import Flask, render_template, redirect
    
    app = Flask(__name__)
    # Flask的配置文件这么玩耍;
    app.config.from_object("settings.DevelopmentConfig")
    
    
    # 添加的第一种方式,推荐使用装饰器的方式;
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        return '# 添加的第一种方式,推荐使用装饰器的方式;'
    
    
    # 添加路由的另外一种方式;
    
    
    def order():
        return '# 添加路由的第二种方式;'
    
    
    app.add_url_rule('/order/', view_func=order)
    if __name__ == '__main__':
        app.run()
    
    if __name__ == '__main__':
        app.run()

    05 Flask框架之反向生成URL

    5.1 Flask框架之反向生成URL,(url_for,endpoint) 

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 12:06
    # File    : 4.反向生成URL.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    from flask import Flask, render_template, redirect, url_for
    
    app = Flask(__name__)
    
    
    # endpoint&url_for不起别名默认就是函数名;
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        v1 = url_for('n1')
        # v2 = url_for('n1')
        # v2 = url_for('n2')
        v2 = url_for('login')
        v3 = url_for('logout')
        print(v1, v2, v3)
        return 'Index'
    
    
    @app.route('/login/', methods=['GET', 'POST'], endpoint='n2')
    def login():
        return 'Login'
    
    
    @app.route('/logout/', methods=['GET', 'POST'], endpoint='n3')
    def logout():
        return 'Logout'

    06 Flask框架之自定义路由转换器

    6.1 Flask之自定义路由转换器; 

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 19:14
    # File    : 6.Flask框架之app.route参数.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask
    
    app = Flask(__name__)
    
    
    # 从旧功能重定向至新功能页面;Js可以做重定向;meta头、js location href
    @app.route('/index/', methods=['GET', 'POST'], redirect_to='/new/')
    def index():
        return '旧的功能'
    
    
    @app.route('/new/', methods=['GET', 'POST'])
    def new():
        return '新功能'
    
    
    if __name__ == '__main__':
        app.run()

    6.2 常见的路由系统;

    • @app.route('/user/<username>')
    • @app.route('/post/<int:post_id>')
    • @app.route('/post/<float:post_id>')
    • @app.route('/post/<path:path>')
    • @app.route('/login', methods=['GET', 'POST']) 
    DEFAULT_CONVERTERS = {
        'default':          UnicodeConverter,
        'string':           UnicodeConverter,
        'any':              AnyConverter,
        'path':             PathConverter,
        'int':              IntegerConverter,
        'float':            FloatConverter,
        'uuid':             UUIDConverter,
    }
    def auth(func):
                def inner(*args, **kwargs):
                    print('before')
                    result = func(*args, **kwargs)
                    print('after')
                    return result
    
            return inner
    
            @app.route('/index.html',methods=['GET','POST'],endpoint='index')
            @auth
            def index():
                return 'Index'def index():
                return "Index"
    
            self.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"])
            or
            app.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"])
            app.view_functions['index'] = index
    
    
            或
            def auth(func):
                def inner(*args, **kwargs):
                    print('before')
                    result = func(*args, **kwargs)
                    print('after')
                    return result
    
            return inner
    
            class IndexView(views.View):
                methods = ['GET']
                decorators = [auth, ]
    
                def dispatch_request(self):
                    print('Index')
                    return 'Index!'
    
            app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))  # name=endpoint
    class IndexView(views.MethodView):
                methods = ['GET']
                decorators = [auth, ]
    
                def get(self):
                    return 'Index.GET'
    
                def post(self):
                    return 'Index.POST'
    
    
            app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))  # name=endpoint
    
    
    
    
            @app.route和app.add_url_rule参数:
                rule,                       URL规则
                view_func,                  视图函数名称
                defaults=None,              默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
                endpoint=None,              名称,用于反向生成URL,即: url_for('名称')
                methods=None,               允许的请求方式,如:["GET","POST"]
                
    
                strict_slashes=None,        对URL最后的 / 符号是否严格要求,
                                            如:
                                                @app.route('/index',strict_slashes=False),
                                                    访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可
                                                @app.route('/index',strict_slashes=True)
                                                    仅访问 http://www.xx.com/index 
                redirect_to=None,           重定向到指定地址
                                            如:
                                                @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
                                                或
                                                def func(adapter, nid):
                                                    return "/home/888"
                                                @app.route('/index/<int:nid>', redirect_to=func)
                subdomain=None,             子域名访问
                                                    from flask import Flask, views, url_for
    
                                                    app = Flask(import_name=__name__)
                                                    app.config['SERVER_NAME'] = 'wupeiqi.com:5000'
    
    
                                                    @app.route("/", subdomain="admin")
                                                    def static_index():
                                                        """Flask supports static subdomains
                                                        This is available at static.your-domain.tld"""
                                                        return "static.your-domain.tld"
    
    
                                                    @app.route("/dynamic", subdomain="<username>")
                                                    def username_index(username):
                                                        """Dynamic subdomains are also supported
                                                        Try going to user1.your-domain.tld/dynamic"""
                                                        return username + ".your-domain.tld"
    
    
                                                    if __name__ == '__main__':
                                                        app.run()
            
    
    a.注册路由原理

    07 Flask框架之app.route参数

    7.1 app.route的常见参数; 

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 19:14
    # File    : 4.Flask框架之app.route参数.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask
    
    app = Flask(__name__)
    
    
    # 从旧功能重定向至新功能页面;Js可以做重定向;meta头、js location href
    @app.route('/index/', methods=['GET', 'POST'], redirect_to='/new/')
    def index():
        return '旧的功能'
    
    
    @app.route('/new/', methods=['GET', 'POST'])
    def new():
        return '新功能'
    
    
    if __name__ == '__main__':
        app.run()

    8 Flask框架之获取子域名的路由

    8.1 域名解析之A记录;

    8.2 本地域名映射之hosts文件;

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 19:14
    # File    : 7.Flask框架之获取子域名.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask
    
    app = Flask(__name__)
    app.config['SERVER_NAME'] = 'www.cuixiaozhao.com:5000'
    
    
    # 从旧功能重定向至新功能页面;Js可以做重定向;meta头、js location href
    @app.route('/dynamic/', methods=['GET', 'POST'], subdomain='<username>')
    def sub_domain(username):
        print(username)
        return '旧的功能1'
    
    
    if __name__ == '__main__':
        app.run()

    09 上述内容总结

    9.1 url;

    9.2 methods;

    9.3 endpoint;

    9.4 @app.route('/index/<int:nid1>/<int:nid2>');

    9.5 url_for;

    10 Flask框架之视图中添加装饰器

    10.1 Flask框架中添加装饰器的注意事项;

    • @装饰器的顺序

    • 引入functools

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 19:14
    # File    : 8.添加装饰器.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask
    import functools
    
    app = Flask(__name__)
    
    
    def wapper(func):
        @functools.wraps(func)
        def inner(*args, **kwargs):
            print('before')
            return func(*args, **kwargs)
    
        return inner
    
    
    @app.route('/xxxx/', methods=['GET', 'POST'])
    @wapper
    def index():
        return 'Index'
    
    
    @app.route('/xxxx/', methods=['GET', 'POST'])
    @wapper
    def order():
        return 'Order'
    
    
    if __name__ == '__main__':
        app.run()

    11 Flask框架之CBV和FBV

    11.1 CBV与FBV;

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 21:57
    # File    : 9.Flask框架之CBV和FBV.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask, redirect, render_template, views
    
    app = Flask(__name__)
    import functools
    
    
    def wapper(func):
        @functools.wraps(func)
        def inner(*args, **kwargs):
            print('before')
            return func(*args, **kwargs)
    
        return inner
    
    
    @app.route('/xxx/', methods=['GET', 'POST'])
    @wapper
    def index():
        return 'Index'
    
    
    class IndexView(views.View):
        methods = ['GET']
        decorators = [wapper, ]
    
        def dispatch_request(self):
            print('Index')
            return 'Index'
    
    
    app.add_url_rule('/index/', view_func=IndexView.as_view(name='index'))  # name == endpoint
    
    
    # CBV方式;
    class IndexView(views.MethodView):
        methods = ['GET']
        decorators = [wapper]
    
        def get(self):
            return 'Index.GET'
    
        def post(self):
            return 'Index POST'
    
    
    app.add_url_rule('/index/', view_func=IndexView.as_view(name='index'))  # name = endpoint
    
    if __name__ == '__main__':
        app.run()

    12 Flask框架之请求和响应相关

    12.1 Flask请求相关之request.xxx;

    12.2 Flask响应相关之return和response;

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 22:13
    # File    : 12.请求和响应.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    from flask import Flask, render_template, request, redirect, jsonify, make_response
    
    app = Flask(__name__)
    
    app.config.from_object("settings.DevelopmentConfig")
    
    
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        # 请求相关;
        request.args
    
        # 响应相关;
        return ''
        return render_template()
        return redirect('/index/')
        # 返回json数据;
        return json.dumps({})  # return jsonify({})
    
    
    if __name__ == '__main__':
        app.run()
    """
            # 请求相关信息;
            # request.method
            # request.args
            # request.form
            # request.values
            # request.cookies
            # 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))
    
            # 响应相关信息;
            # return "字符串"
            # return render_template('html模板路径',**{})
            # return redirect('/index.html')
    
            # response = make_response(render_template('index.html'))
            # response是flask.wrappers.Response类型;
            # response.delete_cookie('key')
            # response.set_cookie('key', 'value')
            # response.headers['X-Something'] = 'A value'
            # return response
    """

    13 Flask框架之模板引擎

    13.1 Flask的templates模板;

    13.2 通Django十分类似,具备block继承、extends;

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-18 22:23
    # File    : 13.模板.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask, render_template, redirect, jsonify, make_response, Markup
    
    app = Flask(__name__)
    
    
    # 全局模板——每个模板均可调用的函数;
    @app.template_global()
    def cxz(a1, a2):
        return a1 + a2
    
    
    def input(value):
        return Markup("<input value:'%s'/>" % value)
    
    
    def gen_input(value):
        return Markup("<input value:'%s'/>" % value)
    
    
    @app.route('/computed/', methods=['GET', 'POST'])
    def computed():
        context = {
            'k1': 123,
            'k2': [11, 22, 33],
            'k3': {'name': 'cuixiaozhao', 'age': 84},
            'k4': lambda x: x + 1,  # 用户写简单的函数;
            'k5': gen_input
        }
        return render_template('index.html', **context)
    
    
    if __name__ == '__main__':
        app.run()

    layout.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    <div>头部</div>
    <div>
        {% block content %}
        {% endblock %}
    </div>
    <div>底部</div>
    </body>
    </html>

    indexx.html;

    {% extends 'layout.html' %}
    
    
    {% block content %}
        <h1>{{ k1 }}</h1>
        <h1>{{ k2.0 }} {{ k2[0] }}</h1>
        <h1>{{ k3.name }} {{ k3['name'] }}{{ k3.get('name',19930911) }}</h1>
        <h1>{{ k4 }}</h1>
        <h1>{{ k5(99) }}</h1>
    
    {% endblock %}

    14 Flask框架之session使用和源码流程(一)

    15 Flask框架之session使用和源码流程(二)

    14.1 & 15.1 之session使用和源码; 

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-19 16:37
    # File    : 1.Flask中的session.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    ""
    """
    Session的请求流程;
    1、请求刚刚到达;
    2、视图函数;
    3、请求结果;
    """
    from flask import Flask, session
    
    app = Flask(__name__)
    
    app.secret_key = 'fdjljfaljfkla'
    
    
    @app.route('/index/')
    def index():
        session['k1'] = 123
        return 'Index'
    
    
    @app.route('/order/')
    def order():
        print(session['k1'])
        return 'Order'
    
    
    if __name__ == '__main__':
        app.run()
    """
    1、Flask
    2、RequestContext
    3、Request
    4、SecureCookieSessionInterface
    5、SecureCookieSession(dict )
    """

    16 Flask框架之before_request和after_request(一)

    16.1 Flask框架中的内置装饰器之before_request和after_request; 

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-19 17:22
    # File    : 1.Flask中内置的特殊装饰器.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask, render_template, redirect
    
    app = Flask(__name__)
    
    
    # before_request和after_request类似于Django中的中间件middleware;
    @app.before_request
    def before_req():
        print('before_request,前')
    
    
    @app.before_request
    def before_req1():
        print('before_request1,前')
    
    
    # request之前,会添加一个reverse反转;
    @app.after_request
    def after_req(response):
        print('after_request, 后')
        return response
    
    
    @app.after_request
    def after_req1(response):
        print('after_request1, 后')
        return response
    
    
    @app.route('/x1/', methods=['GET', 'POST'])
    def x1():
        print('视图函数X1')
        return 'X1'
    
    
    @app.route('/x2/', methods=['GET', 'POST'])
    def x2():
        print('视图函数X2')
        return 'X2'
    
    
    if __name__ == '__main__':
        app.run()

    17 Flask框架之after_request(二)

    17.1 基于before_request的用户登录示例; 

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-19 17:22
    # File    : 2.基于Flask中内置的特殊装饰器做登录验证.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask, render_template, redirect, request, session
    
    app = Flask(__name__)
    app.secret_key = 'fdsjklfdjaslkjflas'
    
    
    # before_request和after_request类似于Django中的中间件middleware;
    @app.before_request
    def check_login():
        if request.path == '/login/':
            return None
        user = session.get('user_info')
        if not user:
            return redirect('/login/')
    
    
    @app.route('/login/', methods=['GET', 'POST'])
    def login():
        return 'Login'
    
    
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        return 'Index'
    
    
    if __name__ == '__main__':
        app.run()

    18 Flask框架字号常见装饰器

    18.1 Flask中其他常见的装饰器;

    • before_first_request
    • before_request
    • after_request
    • teardown_request
    • after_this_request
    • errorhandler(404)
    • ...
    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-19 17:44
    # File    : 3.Flask中其他常见的装饰器.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask, Request, render_template
    
    app = Flask(__name__, template_folder='templates')
    app.debug = True
    
    
    @app.before_first_request
    def before_first_request1():
        print('before_first_request1')
    
    
    @app.before_first_request
    def before_first_request2():
        print('before_first_request2')
    
    
    @app.before_request
    def before_request1():
        Request.nnn = 123
        print('before_request1')
    
    
    @app.before_request
    def before_request2():
        print('before_request2')
    
    
    @app.after_request
    def after_request1(response):
        print('before_request1', response)
        return response
    
    
    @app.after_request
    def after_request2(response):
        print('before_request2', response)
        return response
    
    
    @app.errorhandler(404)
    def page_not_found(error):
        return 'This page does not exist', 404
    
    
    @app.template_global()
    def sb(a1, a2):
        return a1 + a2
    
    
    @app.template_filter()
    def db(a1, a2, a3):
        return a1 + a2 + a3
    
    
    @app.route('/')
    def hello_world():
        return render_template('index.html')
    
    
    if __name__ == '__main__':
        app.run()

    19 上述内容总结

    19.1小结:

      19.1.1配置文件

      19.1.2路由

      19.1.3视图之CBV(class-based views )&FBV(function base view)

      19.1.4request

      19.1.5response = make_response(...)

      19.1.6模板

      19.1.7session

      19.1.8常见的装饰器

    20 Flask之闪现

    20.1 Flask之消息闪现flask& get_flashed_messages;(内部原理基于session实现)

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-19 18:03
    # File    : 1.Flask之消息闪现flush.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    from flask import Flask, session, flash, get_flashed_messages
    
    app = Flask(__name__)
    app.secret_key = 'fdjslkjflkafdaklfjdlakfj'
    
    
    # # 生成session;
    # @app.route('/x1/', methods=['GET', 'POST'])
    # def login():
    #     session['mgs'] = 'cuixiaozhao'
    #     return '视图函数1'
    #
    #
    # # 销毁session;;
    # @app.route('/x2/', methods=['GET', 'POST'])
    # def index():
    #     msg = session.pop('msg')
    #     print(msg)
    #     return '视图函数2'
    # 消息闪现之flask生成session;
    @app.route('/x1/', methods=['GET', 'POST'])
    def login():
        flash('cuixiaozhao', category='x1')
        flash('cuixiaozhao', category='x2')
        return '视图函数1'
    
    
    # 消息闪现之flask销毁session;;
    @app.route('/x2/', methods=['GET', 'POST'])
    def index():
        data = get_flashed_messages(category_filter=['x1', 'x2'])
        print(data)
        return '视图函数2'
    
    
    if __name__ == '__main__':
        app.run()

    21 Flask中间件

    21.1 Flask中自定义中间件; 

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-19 18:13
    # File    : 1.Flask之中间件.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    from flask import Flask
    
    app = Flask(__name__)
    app.secret_key = 'fjaljfdklajfkdasl'
    
    
    @app.route('/x2', methods=['GET', 'POST'])
    def index():
        return 'x2'
    
    
    class MiddleWare(object):
        def __init__(self, old_wsgi_app):
            self.old_wsgi_app = old_wsgi_app
    
        def __call__(self, *args, **kwargs):
            print('before')
            obj = self.old_wsgi_app(*args, **kwargs)
            print('after')
            return obj
    
    
    if __name__ == '__main__':
        app.wsgi_app = MiddleWare(app.wsgi_app)
        app.run()
    """
    1、执行app.__call__方法;
    2、在调用app.wsgi_app方法;
    """

    22 Flask框架之蓝图(一)

      简单来说,Blueprint 是一个存储操作方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求。Flask使用Blueprint让应用实现模块化,在Flask中,Blueprint具有如下属性:

    • 一个应用可以具有多个Blueprint;
    • 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/”、“/sample”或者子域名;
    • 在一个应用中,一个模块可以注册多次;
    • Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的;
    • 在一个应用初始化时,就应该要注册需要使用的Blueprint;

    22.1 创建Flask项目ProFlask;

    22.2 自定义文件存储目录;

    __init__.py;

    from flask import Flask
    
    app = Flask(__name__)
    
    # @app.route('/index/')
    # def index():
    #     pass
    
    
    from .views import account
    from .views import admin
    from .views import user
    
    app.register_blueprint(account.ac)
    app.register_blueprint(admin.ad)
    app.register_blueprint(user.us)

    accounts.py;

    # -*- coding:utf-8 -*-
    # Project: Pro_Flask 
    # Software: PyCharm
    # Time    : 2018-09-19 19:08
    # File    : account.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    from flask import Flask, render_template
    # 蓝图Blueprint;
    from flask import Blueprint
    
    ac = Blueprint('ac', __name__)
    
    
    @ac.route('/login/')
    def login():
        #return 'Login'
        return render_template('login.html')
    
    
    @ac.route('/logout/')
    def logout():
        return 'Logout' 

    23 Flask框架之蓝图(二)

    23.1 蓝图的三大作用(相当于Django中的app);

    • 目录结构的划分;
    • URL的划分;
    • 给每一类URL添加before_request

    24 拓展知识:pipreqs工具

    24.1 项目依赖;

    24.2 拿到代码启动不起来;

    24.3 pip3 install pipreqs;(自动查找项目所需要的依赖包及版本号);

    24.4 pipreqs ./ --force 强制生成依赖文件;

    24.5 pip3 install -i requirements.txt 安装依赖文件;

    25 拓展知识:函数和方法

    25.1 什么是函数?

    25.2 什么是方法?

    # -*- coding:utf-8 -*-
    # Project: Day123 
    # Software: PyCharm
    # Time    : 2018-09-19 20:40
    # File    : 1.函数和方法的区别.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    from types import MethodType, FunctionType
    
    
    class Foo(object):
        def fetch(self):
            pass
    
    
    print(isinstance(Foo.fetch, MethodType))  # False
    print(isinstance(Foo.fetch, FunctionType))  # True
    
    obj = Foo()
    print(obj.fetch)  # <bound method Foo.fetch of <__main__.Foo object at 0x10bbf2358>>
    print(isinstance(obj.fetch, MethodType))  # False
    print(isinstance(Foo.fetch, FunctionType))  # True
    """
    类、对象、方法、函数可以➕();
    方法和函数的区别:被谁来调用!
    """

    第3章 章节三

    01 内容概要

    1.1 Flask提升逼格的时候;

    1.2 Flask的上下文管理;

    1.3 threading.local;

    1.4 数据库连接池;

    02 内容回顾

    2.1 常用的Linux命令(100+);

    2.2 常见算法搞定;

    2.3 数据库连接池;

    2.4 面向对象的特殊方法;

    • call;
    • new;
    • and;
    • equal;
    • next;
    • dict;

    2.5 functools;

    • functools.partial();
    • 装饰器的应用场景;-Flask路由以及before_request、登录认证、Django缓存CSRF_TOKEN;

    2.6 Flask中的蓝图Blueprint;

    2.7 Flask中的session;

    03 threading.local学习

    3.1 threading.local初识;

    # -*- coding:utf-8 -*-
    # Project: Threading 
    # Software: PyCharm
    # Time    : 2018-09-19 22:40
    # File    : 1.ThreadingLocal.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    from threading import local
    from threading import Thread
    import time
    
    # 特殊的对象;
    xiaozhao = local()
    
    
    # xiaozhao = -1
    
    
    def task(arg):
        # global xiaozhao
    
        xiaozhao.value = arg
        time.sleep(2)
        print(xiaozhao.value)
    
    
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

    04 自定义Local思路

    05 自定义Local对象(基于函数)

    5.1 基于get_ident实现;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Threading 
    # Software: PyCharm
    # Time    : 2018-09-20 09:11
    # File    : 3.自定义Local对象(基于函数).py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    from threading import get_ident, Thread
    import time
    
    storage = {}
    
    
    def set(k, v):
        ident = get_ident()
        if ident in storage:
            storage[ident][k] = v
        storage[ident] = {k: v}
    
    
    def get(k):
        ident = get_ident()
        return storage[ident][k]
    
    
    def task(arg):
        set('val', arg)
        print(storage)
        time.sleep(2)
        v = get('val')
        print(v)
    
    
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

    06 自定义Local对象(基于面向对象)

    6.1 基于面向对象basic;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Threading 
    # Software: PyCharm
    # Time    : 2018-09-20 09:20
    # File    : 4.自定义Local对象(基于面向对象).py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from threading import get_ident
    from threading import Thread
    
    
    class Local(object):
        storage = {}
    
        def set(self, k, v):
            ident = get_ident()
            if ident in Local.storage:
                Local.storage[ident][k] = v
            else:
                Local.storage[ident] = {k: v}
    
        def get(self, k):
            ident = get_ident()
            return Local.storage[ident][k]
    
    obj = Local()
    def task(arg):
        obj.set('val', arg)
        v = obj.get('val')
        print(v)
    
    
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

    6.2 基于面向对象优化版;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Threading 
    # Software: PyCharm
    # Time    : 2018-09-20 09:20
    # File    : 5.自定义Local对象(基于面向对象优化版 ).py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from threading import get_ident
    from threading import Thread
    
    
    class Local(object):
    
        def __setattr__(self, k, v):
            # self.storage = {}
            object.__setattr__(self, 'storage', {})
            ident = get_ident()
            if ident in self.storage:
                self.storage[ident][k] = v
            else:
                self.storage[ident] = {k: v}
    
    
    def __getattr__(self, item):
        ident = get_ident()
        return self.storage[ident][item]
    
    
    obj = Local()
    obj1 = Local()
    
    
    def task(arg):
        obj.val = arg
        obj1.val = arg
        print(obj.val)
    
    
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

    6.3 基于面向对象升级版;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Threading 
    # Software: PyCharm
    # Time    : 2018-09-20 09:20
    # File    : 4.自定义Local对象(基于面向对象).py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from threading import get_ident
    from threading import Thread
    
    
    class Local(object):
        storage = {}
    
        def __setattr__(self, k, v):
            ident = get_ident()
            if ident in Local.storage:
                Local.storage[ident][k] = v
            else:
                Local.storage[ident] = {k: v}
    
        def __getattr__(self, item):
            ident = get_ident()
            return Local.storage[ident][item]
    
    
    obj = Local()
    
    
    def task(arg):
        obj.val = arg
        print(obj.val)
    
    
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

    6.4 基于面向对象greenlet版本; 

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Threading 
    # Software: PyCharm
    # Time    : 2018-09-20 09:20
    # File    : 7.自定义Local对象(基于greenlet).py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    try:
        from greenlet import getcurrent as get_ident
    except Exception as e:
        from threading import get_ident
    from threading import Thread
    
    
    class Local(object):
        storage = {}
    
        def __setattr__(self, k, v):
            ident = get_ident()
            if ident in Local.storage:
                Local.storage[ident][k] = v
            else:
                Local.storage[ident] = {k: v}
    
        def __getattr__(self, item):
            ident = get_ident()
            return Local.storage[ident][item]
    
    
    obj = Local()
    
    
    def task(arg):
        obj.val = arg
        print(obj.val)
    
    
    for i in range(10):
        t = Thread(target=task, args=(i,))
        t.start()

    07 Flask上下文管理之本质分析 

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Threading 
    # Software: PyCharm
    # Time    : 2018-09-20 09:45
    # File    : 1.Flask源码分析上下文管理.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def hello_world():
        return 'cuixiaozhao!'
    
    
    if __name__ == '__main__':
        app.__call__
        app.run()
    
        ""
    """
    1、第一个阶段:将ctx(request,session)放到Local对象;
    2、第二阶段:视图函数导入:request、session;
    3、请求处理完毕:
        -获取session并保存到cookie;
        -将ctx删除;
    """

    08 Flask上下文管理之请求到处理阶段

    8.1 Flask中的session是何时创建何时销毁的?request_context,localstack、local;

    09 Flask上下文管理之视图调用阶段

    9.1 调用阶段;

    10 Flask上下文管理之视图调动阶段和结束阶段

    10.1 视图调用阶段和结束阶段;

    11 问题来了

    11.1 Flask中一共有几个Local和Local对象?

    都是2个;

    12 Flask中的g到底是什么呢?生命周期?

    12.1 Flask的g对象范围;

    13 内容补充:面向对象的私有字段

    13.1 私有字段不建议去调动;

    • obj = Foo()
    • obj._Foo_age(类名前加下划线)

    14 homework

    14.1 按照组为单位,画图-类的调用关系图;

    第4章 章节四

    01 内容概要

    1.1 flask-session;

    1.2 单独模块-数据库连接池DBUtils;

    1.3 原生SQL(基于pymysql)还是ORM好?!

    1.4 wtforms(任何Django框架都可使用);

    1.5 SQLAchemy、flask-sqlachemy;

    1.6 flask-script;

    1.7 flask-migrate;

    02 内容回顾

    2.1 谈谈Django和Flask的认识?

    2.2 Django的上下文管理机制?

    2.3 ctx = RequestContext(request,session)

    2.4 Local对象的作用?

    • 看过Local源码,和Threading.local相似但又有不同之处;
    • 不同之处在于,Local中基于greenlet获取唯一表示,颗粒度更细;

    2.5 为什么使用Localstack?对Local对象中的数据进行操作。

    2.6 上下文管理分为两个-请求上下文和App上下文

    2.7 什么是g?一次请求周期内的全局变量。

    2.8 获取session和g的流程。

    2.9 Flask中的技术点。

    • 反射;
    • 面向对象-封装、继承和多态;
    • __dict__;
    • 线程相关的东西-threading.local;
    • 自己写一个类+列表,实现一个栈(基于Localstack实现栈);

    03 路飞学城补充:视频播放授权

    4.1 CC视频播放授权;

    04 flask-session组件使用和原理(一)

     4.1 flask-session;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 16:14
    # File    : 1.flask_session.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask, session
    from flask_session import RedisSessionInterface
    
    app = Flask(__name__)
    app.secret_key = 'fdahfdafdajfalk'
    
    # 默认session保存操作;
    # from flask.sessions import SecureCookieSessionInterface
    
    # app.session_interface = SecureCookieSessionInterface()
    
    # 使用Redis保存session;
    from flask.ext.session import Session
    from redis import Redis
    
    app.config['SESSION_TYPE'] = 'redis'
    app.config['SESSION_REDIS'] = Redis(host='192.168.0.94', port='6379')
    app.session_interface = RedisSessionInterface(
        redis=Redis(host='127.0.0.1', port=6379),
        key_prefix='flaskxxxx'
    
    )
    
    
    @app.route('/login/')
    def login():
        session['k1'] = 123
        return 'Login'
    
    
    @app.route('/index/')
    def index():
        v = session['k1']
        print(v)
        return 'Index'
    
    
    if __name__ == '__main__':
        app.run()

    05 flask-session组件使用和原理(二)

    5.1 原理难,实现的机制难,使用起来比较容易,但是要想学好, 必须懂原理;

    5.2 MTV 和MVC设计模式;

    • MTV-Model、Templates、View;
    • MVC-Model、View、Controller;

    5.3 自定义Flask项目Flask_Session;

    __init__.py;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 16:46
    # File    : __init__.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask
    from .views import account
    from .views import home
    
    
    def create_app():
        app = Flask(__name__)
        app.config.from_object('settings.DevelopmentConfig')
        app.register_blueprint(account.account)
        app.register_blueprint(home.home)
        return app

    manage.py;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 16:46
    # File    : manage.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    from Flask_Session import create_app
    
    app = create_app()
    
    if __name__ == '__main__':
        app.run()

    account.py;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 16:53
    # File    : account.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    from flask import Blueprint, render_template, request, session, redirect
    from uuid import uuid4
    
    account = Blueprint('account', __name__)
    
    
    @account.route('/login/', methods=['GET', 'POST'])
    def login():
        if request.method == "GET":
            return render_template("login.html")
        user = request.form.get('user')
        pwd = request.form.get('pwd')
        if user == "cxz" and pwd == "123":
            uid = str(uuid4())
            session.permanent = True
            session['user_info'] = {'id': uid, 'name': user}
            return redirect('/index/')
        else:
            return render_template('login.html', msg='用户名或者密码错误!')

    home.py;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 17:33
    # File    : home.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    from flask import Blueprint, render_template, request, session, redirect
    
    home = Blueprint('home', __name__)
    
    
    @home.route('/index/', methods=['GET', 'POST'])
    def index():
        user_info = session.get('user_info')  # {'k1':1,'k2':2}
        print("原来的值", user_info)
        session['user_info']['k1'] = 19939
        user_info = session.get('user_info')
        print("修改之后的值", user_info)
        # session['modified'] = True,在配置文件中使用SESSION_REFRESH_EACH_REQUEST代替;
        return 'Index'
    
    
    @home.route('/test/')
    def test():
        user_info = session.get('user_info')
        print(user_info)
        return 'Test'

    settings.py;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 17:28
    # File    : settings.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from datetime import timedelta
    
    
    class Config(object):
        DEBUG = True
        TESTING = False
        DATABASE_URI = 'sqlite://:memory'
        SECRET_KEY = 'fjdksjfdasljflksd'
        PERMANENT_SESSION_LIFETIME = timedelta(minutes=20)
        SESSION_REFRESH_EACH_REQUEST = True
    
    
    class ProductionConfig(Config):
        pass
    
    
    class DevelopmentConfig(Config):
        pass
    
    
    class TestingConfig(Config):
        pass

    06 flask-session组件使用和原理(三)

    6.1 将数据存储至Redis;

    settings.py;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 17:28
    # File    : settings.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from datetime import timedelta
    from redis import Redis
    
    
    class Config(object):
        DEBUG = True
        TESTING = False
        DATABASE_URI = 'sqlite://:memory'
        SECRET_KEY = 'fjdksjfdasljflksd'
        PERMANENT_SESSION_LIFETIME = timedelta(minutes=20)
        SESSION_REFRESH_EACH_REQUEST = True
        SESSION_TYPE = "redis"
        # SESSION_REDIS = Redis(host='127.0.0.1',port='6379')
    
    
    class ProductionConfig(Config):
        SESSION_REDIS = Redis(host='127.0.0.1', port='6379')
    
    
    class DevelopmentConfig(Config):
        SESSION_REDIS = Redis(host='127.0.0.1', port='6379')
    
    
    class TestingConfig(Config):
        SESSION_REDIS = Redis(host='127.0.0.1', port='6379')

    __init__.py;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 16:46
    # File    : __init__.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    from flask import Flask
    from .views import account
    from .views import home
    # from flask.ext.session import Session
    from flask_session import Session
    
    
    def create_app():
        app = Flask(__name__)
        app.config.from_object('settings.DevelopmentConfig')
        app.register_blueprint(account.account)
        app.register_blueprint(home.home)
        # 将Session替换成Redis;
        Session(app)
        return app

    6.2 Mac版本的Redis可视化工具redis desktop manager的使用;

    07 flask-session组件使用和原理(四)

    7.1 flask-session的作用:将默认保存的签名cookie中的值保存到Redis、memcached、file、MongoDB、SQLAchemy;

    7.2 配置方法;

    app.config['SESSION_TYPE'] = 'redis'

    app.config['SESSION_REDIS'] = Redis(host = '127.0.0.1',port=6379)

    7.3 替换方式;

    from flask_session import Session

    Session(app)

    7.4 注意事项:session中存储的是字典,修改字典内部元素时候,会造成数据不更新;

    • modified = True ,默认值为False
    • SESSION_REFRESH_EACH_REQUEST = True and session.permanent = True (Redis中默认)

    08 基于pymysql实现用户登录

    8.1 基于pymysql;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 20:13
    # File    : sql.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    import pymysql
    
    
    class SQLHelper(object):
        @staticmethod
        def open():
            conn = pymysql.connect(host='mysql123.cuixiaozhao.com', port=3306, user='root', passwd='Tqtl911!@%*)',
                                   db='flask_session')
            cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
            return conn, cursor
    
        @staticmethod
        def close(conn, cursor):
            conn.commit()
            cursor.close()
            conn.close()
    
        @classmethod
        def fetch_one(cls, sql, args):
            # cursor.execute("select id,name from users where name = %s and pwd = %s", ['cxz', '123', ])
            conn, cursor = cls.open()
            cursor.execute(sql, args)
            obj = cursor.fetchone()
            cls.close(conn, cursor)
            return obj
    
        @classmethod
        def fetch_all(cls, sql, args):
            conn, cursor = cls.open()
            cursor.execute(sql, args)
            obj = cursor.fetchall()
            cls.close(conn, cursor)
            return obj

    8.2 pymysql的练习和使用; 

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-20 19:53
    # File    : MySQL数据库练习.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    import pymysql
    
    conn = pymysql.connect(host='x.x.x.x', port=3306, user='root', passwd='Tqtl911!@%*)', db='flask_session')
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("select id,name from users where name = %s and pwd = %s", ['cxz', '123', ])
    obj = cursor.fetchone()
    conn.commit()
    cursor.close()
    
    conn.close()
    
    print(obj)

    09 基于数据库连接池实现用户登录(出bug)

    9.1 DBUtils模块初识;

      DBUtils是Python的一个用于实现数据库连接池的模块。

    pool.py;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-21 10:26
    # File    : pool.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    import time
    import pymysql
    import threading
    from DBUtils.PooledDB import PooledDB, SharedDBConnection
    from manage import app
    
    POOL = PooledDB(
        creator=pymysql,  # 使用链接数据库的模块;
        maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数;
        mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建;
        maxcached=5,  # 链接池中最多闲置的链接;
        maxshared=3,
        # 链接池中最多共享的链接数量,0和None表示全部共享。PS: Useless无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
        blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错;
        maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制;
        setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
        ping=0,
        # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
        host='mysql.cuixiaozhao.com',
        port=3306,
        user='root',
        password='Tqtl911!@%*)',
        database='flask_session',
        charset='utf8'
    )

    9.2 模式一:模式一:为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程再次使用。当线程终止时,连接自动关闭。

    POOL = PersistentDB(
        creator=pymysql,  # 使用链接数据库的模块
        maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
        setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
        ping=0,
        # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
        closeable=False,
        # 如果为False时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。如果为True时, conn.close()则关闭链接,那么再次调用pool.connection时就会报错,因为已经真的关闭了连接(pool.steady_connection()可以获取一个新的链接)
        threadlocal=None,  # 本线程独享值得对象,用于保存链接对象,如果链接对象被重置
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123',
        database='pooldb',
        charset='utf8'
    )
    
    def func():
        conn = POOL.connection(shareable=False)
        cursor = conn.cursor()
        cursor.execute('select * from tb1')
        result = cursor.fetchall()
        cursor.close()
        conn.close()
    
    func()

    9.3 模式二:模式二:创建一批连接到连接池,供所有线程共享使用。

    import time
    import pymysql
    import threading
    from DBUtils.PooledDB import PooledDB, SharedDBConnection
    POOL = PooledDB(
        creator=pymysql,  # 使用链接数据库的模块
        maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
        mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
        maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
        maxshared=3,  # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
        blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
        maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
        setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
        ping=0,
        # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123',
        database='pooldb',
        charset='utf8'
    )
    
    
    def func():
        # 检测当前正在运行连接数的是否小于最大链接数,如果不小于则:等待或报raise TooManyConnections异常
        # 否则
        # 则优先去初始化时创建的链接中获取链接 SteadyDBConnection。
        # 然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。
        # 如果最开始创建的链接没有链接,则去创建一个SteadyDBConnection对象,再封装到PooledDedicatedDBConnection中并返回。
        # 一旦关闭链接后,连接就返回到连接池让后续线程继续使用。
        conn = POOL.connection()
    
        # print(th, '链接被拿走了', conn1._con)
        # print(th, '池子里目前有', pool._idle_cache, '
    ')
    
        cursor = conn.cursor()
        cursor.execute('select * from tb1')
        result = cursor.fetchall()
        conn.close()
    
    
    func()

    9.4 补充说明; 如果没有连接池,使用pymysql来连接数据库时,单线程应用完全没有问题,但如果涉及到多线程应用那么就需要加锁,一旦加锁那么连接势必就会排队等待,当请求比较多时,性能就会降低了。

    10 解决bug(一)

    11 解决bug(二)

    12 数据库连接池总结

    12.1 每一个线程创建一个连接,关闭(默认不关闭),线程终止时,才关闭连接;

    12.2 创建共享连接池;

    12.3 应用;只要写原生sql,使用了pymysql就得使用数据库连接池DBUtils;

    13 WTforms介绍以及用户登录示例

    13.1 WTform安装;

    13.2 WTform的使用;

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Flask, render_template, request, redirect
    from wtforms import Form
    from wtforms.fields import core
    from wtforms.fields import html5
    from wtforms.fields import simple
    from wtforms import validators
    from wtforms import widgets
    
    app = Flask(__name__, template_folder='templates')
    app.debug = True
    
    
    class LoginForm(Form):
        name = simple.StringField(
            label='用户名',
            validators=[
                validators.DataRequired(message='用户名不能为空.'),
                validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
            ],
            widget=widgets.TextInput(),
            render_kw={'class': 'form-control'}
    
        )
        pwd = simple.PasswordField(
            label='密码',
            validators=[
                validators.DataRequired(message='密码不能为空.'),
                validators.Length(min=8, message='用户名长度必须大于%(min)d'),
                validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[$@$!%*?&])[A-Za-zd$@$!%*?&]{8,}",
                                  message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
    
            ],
            widget=widgets.PasswordInput(),
            render_kw={'class': 'form-control'}
        )
    
    
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'GET':
            form = LoginForm()
            return render_template('login.html', form=form)
        else:
            form = LoginForm(formdata=request.form)
            if form.validate():
                print('用户提交数据通过格式验证,提交的值为:', form.data)
            else:
                print(form.errors)
            return render_template('login.html', form=form)
    
    if __name__ == '__main__':
        app.run()
    
    app.py

    index.html;

    !DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>登录</h1>
    <form method="post">
        <!--<input type="text" name="name">-->
        <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>
    
        <!--<input type="password" name="pwd">-->
        <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
        <input type="submit" value="提交">
    </form>
    </body>
    </html>

    14 WTforms用户注册示例

    14.1 基于WTforms做用户注册验证;

    app.py;

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # Project: Flask_Session 
    # Software: PyCharm
    # Time    : 2018-09-21 15:11
    # File    : register.py
    # Author  : 天晴天朗
    # Email   : tqtl@tqtl.org
    
    
    from flask import Flask, render_template, request, redirect, Blueprint
    from wtforms import Form
    from wtforms.fields import core
    from wtforms.fields import html5
    from wtforms.fields import simple
    from wtforms import validators
    from wtforms import widgets
    
    app = Flask(__name__, template_folder='templates')
    app.debug = True
    
    
    class RegisterForm(Form):
        name = simple.StringField(
            label='用户名',
            validators=[
                validators.DataRequired('用户名不能为空!')
            ],
            widget=widgets.TextInput(),
            render_kw={'class': 'form-control'},
            default='alex'
        )
    
        pwd = simple.PasswordField(
            label='密码',
            validators=[
                validators.DataRequired(message='密码不能为空.')
            ],
            widget=widgets.PasswordInput(),
            render_kw={'class': 'form-control'}
        )
    
        pwd_confirm = simple.PasswordField(
            label='重复密码',
            validators=[
                validators.DataRequired(message='重复密码不能为空.'),
                validators.EqualTo('pwd', message="两次密码输入不一致")
            ],
            widget=widgets.PasswordInput(),
            render_kw={'class': 'form-control'}
        )
    
        email = html5.EmailField(
            label='邮箱',
            validators=[
                validators.DataRequired(message='邮箱不能为空.'),
                validators.Email(message='邮箱格式错误')
            ],
            widget=widgets.TextInput(input_type='email'),
            render_kw={'class': 'form-control'}
        )
    
        gender = core.RadioField(
            label='性别',
            choices=(
                (1, '男'),
                (2, '女'),
            ),
            coerce=int
        )
        city = core.SelectField(
            label='城市',
            choices=(
                ('bj', '北京'),
                ('sh', '上海'),
            )
        )
    
        hobby = core.SelectMultipleField(
            label='爱好',
            choices=(
                (1, '篮球'),
                (2, '足球'),
            ),
            coerce=int
        )
    
        favor = core.SelectMultipleField(
            label='喜好',
            choices=(
                (1, '篮球'),
                (2, '足球'),
            ),
            widget=widgets.ListWidget(prefix_label=False),
            option_widget=widgets.CheckboxInput(),
            coerce=int,
            default=[1, 2]
        )
    
        def __init__(self, *args, **kwargs):
            super(RegisterForm, self).__init__(*args, **kwargs)
            self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
    
        def validate_pwd_confirm(self, field):
            """
            自定义pwd_confirm字段规则,例:与pwd字段是否一致
            :param field:
            :return:
            """
            # 最开始初始化时,self.data中已经有所有的值
    
            if field.data != self.data['pwd']:
                # raise validators.ValidationError("密码不一致") # 继续后续验证
                raise validators.StopValidation("密码不一致")  # 不再继续后续验证
    
    
    @app.route('/register', methods=['GET', 'POST'])
    def register():
        if request.method == 'GET':
            form = RegisterForm(data={'gender': 1})
            return render_template('register.html', form=form)
        else:
            form = RegisterForm(formdata=request.form)
            if form.validate():
                print('用户提交数据通过格式验证,提交的值为:', form.data)
            else:
                print(form.errors)
            return render_template('register.html', form=form)
    
    
    if __name__ == '__main__':
        app.run()
    
    # app.py

    register.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>用户注册</h1>
    <form method="post" novalidate style="padding:0  50px">
        {% for item in form %}
        <p>{{item.label}}: {{item}} {{item.errors[0] }}</p>
        {% endfor %}
        <input type="submit" value="提交">
    </form>
    </body>
    </html>

    15 内容总结

    15.1 WTforms总结;

    WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。

    16 homework

    16.1 补充了路飞学城CC授权播放;

    16.2 flask-session的用法以及Redis的使用;

    16.3 pymysql以及数据库连接池DBUtils;

    16.4 WTforms;

    16.5 Flask的上下文管理以及源码

    16.6 flask-session;

     

     

  • 相关阅读:
    MutationObserver 简单应用场景
    call apply bind sleep
    js 继承,Object.setPrototypeOf | Object.getPrototypeOf | Object.create class
    JSON.stringify
    javascript 与node的 event-loop
    js 不常用面试题 数组对象深度取值
    Oracle单表备份
    mybatis批量写法
    mybatis批量更新
    Python中if __name__ == '__main__':理解
  • 原文地址:https://www.cnblogs.com/tqtl911/p/9581953.html
Copyright © 2011-2022 走看看