• 0043 工程通用工具编写


      本项目的通用工具都存放在GeneralTools目录下,主要包括以下内容:

    01 更改JWT Token交接方式(Authentication.py)

      按JWT官方要求,JWT Token必须前端携带在Header提交。这样提交更安全,但前端每次提交数据请求的时候,都必须去获取Token,然后包装在Header里,特别实现网页跳转的时候,非常不便,因此需要写一个类去覆盖默认从Header中取Token的方法。直接把Token从session中获取。这样就不用前端去处理Token了,而是后端直接从session里拿就行了。

    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    
    
    class GetAuthentication(JSONWebTokenAuthentication):
        def get_jwt_value(self, request):
            
            # 从session中获取token
            # return request.session.get('token')
            # 从url中获取token
            return request.query_params.get('token')
    

    02 获取和检查Access_Token(AuthToken)

    from itsdangerous import TimedJSONWebSignatureSerializer as TJWSSerializer
    from itsdangerous import BadData
    from TongHeng2 import settings
    from GeneralTools import Constents
    import logging
    
    
    logger = logging.getLogger('tongheng2')
    
    
    def getToken(openid, mobile):
        """
        【功能说明】根据用户openid和mobile用于生成access_token
        """
        tjwserializer = TJWSSerializer(
            secret_key=settings.SECRET_KEY,  # 密钥
            salt=Constents.SALT,  # 盐值
            expires_in=Constents.VERIFY_ACCESS_TOKEN_EXPIRES  # 有效期
        )
        access_token = tjwserializer.dumps({'openid': openid, 'mobile': mobile})  # bytes
        access_token = access_token.decode()
        return access_token
    
    
    def checkToken(token, request):
        """
        【功能说明】检查access_token是否正确
        """
        tjwserializer = TJWSSerializer(
            secret_key=settings.SECRET_KEY,  # 密钥
            salt=Constents.SALT,  # 盐值
            expires_in=Constents.VERIFY_ACCESS_TOKEN_EXPIRES  # 有效期
        )
        try:
            tjwdata = tjwserializer.loads(token)
            # 如果验证成功,则把手机号存入到session里面,以便在页面中可以随时根据mobile获取用户信息和权限。
            mobile = tjwdata['mobile']
            request.session['mobile'] = mobile
            return True
        except BadData as e:
            logger.error(e)
            return False

    03 生成模型抽象类(BaseModel)

      本项目中所有模型都需要创建时间和更新时间两个字段,为了避免每个模型都去创建这两个字段,我们生成一个抽象类,所有模型都继承这个类,从而自动产生创建时间和更新时间两个字段。

    from django.db import models
    
    
    class BaseModel(models.Model):
        create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', help_text='创建时间')
        update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间', help_text='更新时间')
    
        class Meta:
            # 说明这个类是一个抽象模型类,在迁移的时候不会生成表
            abstract = True
    

    04 常量文件(Constants.py)

      本项目中所有的常量,全存储在这个文件里。

    05 生成schemas概要(CustomSchema.py)

      项目中经常会用到临时字段用于前后端交互,这些字段,不需要存在数据库里,只是前后端交互的一个变量。这时候,没必要再去写序列化器。直接就用临时字段。

    from rest_framework.schemas import AutoSchema
    
    
    class CustomSchema(AutoSchema):
        """
        自定义AutoSchema,为view手动添加注释
        """
    
        def get_manual_fields(self, path, method):
            """
            location有下列可选选项可以选:
            path 包含在模板化URI中。例如,url值/products/{product_code}/可以与"path"字段一起使用。
            query 包含在URL查询参数中。例如?search=sale。通常用于GET请求。
            form 包含在请求正文中,作为JSON对象或HTML表单的单个项目。例如{"colour": "blue", ...}。通常的POST,PUT和PATCH请求。"form"单个链接上可以包含多个字段。
            header 包含在请求头中,可以自定义。
            {
                'get': [
                    coreapi.Field(name="mobile", required=True, location="path", schema=coreschema.String(description='手机号')),
                    coreapi.Field(name="name", required=True, location="query", schema=coreschema.String(description='用户名')),
                    coreapi.Field(name="password", required=True, location="query", schema=coreschema.String(description='密码')),
                ],
                'post': [
                    coreapi.Field(name="mobile", required=True, location="path", schema=coreschema.String(description='手机号')),
                    coreapi.Field(name="subject", required=True, location="query", schema=coreschema.String(description='邮件主题')),
                    coreapi.Field(name="message", required=True, location="query", schema=coreschema.String(description='邮件正文')),
                    coreapi.Field(name="to_email", required=True, location="query", schema=coreschema.String(description='收件人')),
                ],
            }
            """
    
            # 可能是list,也可能是dict
            manual_fields = super(CustomSchema, self).get_manual_fields(path, method)
    
            if type(manual_fields) == list:
                return manual_fields
            else:
                # dict
                for k, v in self._manual_fields.items():
                    if method.lower() == k.lower():
                        return v
                else:
                    return []
    

    06 微信认证装饰器(Decorate.py)

    from rest_framework.response import Response
    from rest_framework import status
    from django.shortcuts import redirect
    from .AuthToken import checkToken
    from .WeChatOAuth import get_WeChatOAuth
    from . import Constents
    
    
    # 装饰器
    def decorate(func):
        def wrapper(request, *args, **kwargs):
            # 从用户session中,获取access_token
            access_token = request.session.get('access_token', None)
            if access_token and checkToken(access_token, request):
                # 如果access_token存在,且正确,则直接执行下一步
                return func(request, *args, **kwargs)
            else:
                # 如果access_token不存在,或不正确
                userAgent = str(request.META['HTTP_USER_AGENT'])  # 获取访问浏览器的类型
                if userAgent.find('MicroMessenger') < 0:  # 如果不是微信浏览器,则直接跳转到登录页面
                    return redirect('/Organizations/Login/')
                else:  # 如果是微信浏览器,返回微信授权回调地址,前端根据地址,调用login登录页面。
                    url = get_WeChatOAuth(Constents.REDIRECT_URI).authorize_url
                    return Response(data={'url': url}, status=status.HTTP_201_CREATED)
    
        return wrapper
    

    07 自定义异常(Exceptions.py)

    from rest_framework.views import exception_handler as drf_exception_handler
    import logging
    from django.db import DatabaseError
    from redis.exceptions import RedisError
    from rest_framework.response import Response
    from rest_framework import status
    
    # 获取在配置文件中定义的logger,用来记录日志
    logger = logging.getLogger('tongheng2')
    
    
    def exception_handler(exc, context):
        """
        自定义异常处理
        :param exc: 异常
        :param context: 抛出异常的上下文
        :return: Response响应对象
        """
        # 调用drf框架原生的异常处理方法
        response = drf_exception_handler(exc, context)
    
        if response is None:
            view = context['view']
            if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
                # 数据库异常
                logger.error('[%s] %s' % (view, exc))
                response = Response({'message': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
    
        return response
    

    08 更改Django文件存储方式为fastDFS(FastDFSStorage.py)

    from django.conf import settings
    from django.core.files.storage import Storage
    from django.utils.deconstruct import deconstructible
    from fdfs_client.client import Fdfs_client
    import os
    
    
    @deconstructible
    class FastDFSStorage(Storage):
        def __init__(self, base_url=None, client_conf=None):
            """
            初始化
            :param base_url: 用于构造图片完整路径使用,图片服务器的域名
            :param client_conf: FastDFS客户端配置文件的路径
            """
            if base_url is None:
                base_url = settings.FDFS_URL
            self.base_url = base_url
            if client_conf is None:
                client_conf = settings.FDFS_CLIENT_CONF
            self.client_conf = client_conf
    
        def _open(self, name, mode='rb'):
            """
            用不到打开文件,所以省略
            """
            pass
    
        def _save(self, name, content):
            """
            在FastDFS中保存文件
            :param name: 传入的文件名
            :param content: 文件内容
            :return: 保存到数据库中的FastDFS的文件名
            """
            client = Fdfs_client(self.client_conf)
            # 告诉fastDFS服务器,返回文件的扩展名。
            ext_name = os.path.splitext(content.name)[1][1:]
            ret = client.upload_by_buffer(content.read(), ext_name)
            if ret.get("Status") != "Upload successed.":
                raise Exception("upload file failed")
            file_name = ret.get("Remote file_id")
            # 必须替换路径中的分隔符,否则,查询不到上传的文件。
            file_name = str(file_name).replace('\', '/')
            return file_name
    
        def url(self, name):
            """
            返回文件的完整URL路径
            :param name: 数据库中保存的文件名
            :return: 完整的URL
            """
            if name.startswith('http'):
                return name
            else:
                return self.base_url + name
    
        def exists(self, name):
            """
            判断文件是否存在,FastDFS可以自行解决文件的重名问题
            所以此处返回False,告诉Django上传的都是新文件
            :param name:  文件名
            :return: False
            """
            return False
    

    09 更改JWT返回值(JwtHandler.py)

    import logging
    
    # 获取在配置文件中定义的logger,用来记录日志
    # 注:其中的tongheng2必须和配置文件中指定的配置路径一致。
    logger = logging.getLogger('tongheng2')
    
    
    def jwt_response_payload_handler(token, user=None, request=None):
        """
        【功能描述】直接使用DRF-JWT提供的视图方法时,其默认的返回值只有token,若需要前端接收到用户其它信息,
        需要重写jwt_response_payload_handler方法。
        """
    
        return {
            'id': user.id,
            'username': user.username,
            'photo_url': user.photo_url,
            'mobile': user.mobile,
            'openid': user.openid,
            'token': token
        }
    

    10 翻页设置(Paginations.py)

    from rest_framework.pagination import PageNumberPagination
    
    
    class SetPageSize5(PageNumberPagination):
        page_size = 5
        page_size_query_param = 'page_size'
    
    
    class SetPageSize10(PageNumberPagination):
        page_size = 10
        page_size_query_param = 'page_size'
    
    
    class SetPageSize15(PageNumberPagination):
        page_size = 15
        page_size_query_param = 'page_size'
    

    11 Redis数据库连接(Redis.py)

    VERSION = (4, 11, 0)
    __version__ = '.'.join(map(str, VERSION))
    
    
    def get_redis_connection(alias='default', write=True):
        """
        Helper used for obtaining a raw redis client.
        """
    
        from django.core.cache import caches
    
        cache = caches[alias]
    
        if not hasattr(cache, "client"):
            raise NotImplementedError("This backend does not support this feature")
    
        if not hasattr(cache.client, "get_client"):
            raise NotImplementedError("This backend does not support this feature")
    
        return cache.client.get_client(write)
    

    12 正则验证函数(Verifications.py)

    import re
    
    
    def mobileVerify(mobile):
        if re.match(r'^1[3-9]d{9}$', mobile):
            return True
        return False
    

    13 获取Wechatpy对象(WechatOAuth.py)

    from GeneralTools import Constents
    from wechatpy.oauth import WeChatOAuth
    
    
    def get_WeChatOAuth(redirect_uri, state='123', scope='snsapi_userinfo'):
        """
        获取WeChatOAuth对象
        :param redirect_uri: 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
        :param scope:应用授权作用域,snsapi_base,snsapi_userinfo
        :param state:重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
        :return: WeChatOAuth对象
        """
    
        return WeChatOAuth(
            app_id=Constents.WECHAT_APPID,
            secret=Constents.WECHAT_APPSECRET,
            redirect_uri=redirect_uri,
            scope=scope,
            state=state
        )
    

      

  • 相关阅读:
    jQuery 笔记
    centos 项目上线shell脚本
    linux关于用户密码家目录总结
    python 写了一个批量拉取文件进excel文档
    css 选择器/table属性/type 属性
    表单
    html table
    html超文本标记语言
    mysql数据库1
    mysql数据库
  • 原文地址:https://www.cnblogs.com/dorian/p/12430851.html
Copyright © 2020-2023  润新知