【flask】开发记录

flask 问题记录

flask request传递发送文件数据。

前端发送文件数据到一个服务,由此服务转发到另外一个服务。
requests.request 方法 files 参数说明

files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
        ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
        or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
        defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
        to add for the file.
  1. 从前端获取文件列表数据:
    file_list = request.files.getlist(“file”)
    for file in file_list:
    content = file.read()
    files_dict[file.filename] = (file.filename, content, file.content_type)
    从form表单获取数据:
    data = request.form.to_dict()
  2. 转发files数据
    response = requests.request(method, endpoint + url, headers=headers, files=files_dict, data=data)

db session 超时

在异步处理数据时,如果进入异步操作时,没有执行db.session.close(),可能导致数据库长连接而报错。
因此在进入长时间的异步操作,请记住关闭当前数据库连接。

flask tls 启动 版本 加密算法设置

待补充

使用chardet解码大文件可能内存泄露,时间超长。建议使用cchardet。

https://github.com/chardet/chardet/issues/29

contains 查询注意通配符 % _

Projects.name.contains(name)
如果不对通配符做另外处理,前端查询 _ % 会查询出所有内容
Projects.name.contains(name,autoescape=True)
https://docs.sqlalchemy.org/en/14/core/type_basics.html#sql-standard-and-multiple-vendor-types
https://www.w3schools.com/sql/sql_like.asp

log打印

‘propagate’: False # True 的时候会执行root 的handle
https://www.cnblogs.com/cwp-bg/p/8946394.html
https://www.pylenin.com/blogs/python-logging-guide/#:~:text=aka%20Root%20logger.-,Root%20Logger,are%20using%20the%20root%20logger.

import os
import stat
from logging.config import dictConfig
from logging.handlers import RotatingFileHandler


class IngestionRotatingFileHandler(RotatingFileHandler):

    def doRollover(self):
        if self.stream:
            self.stream.close()
            self.stream = None
        if self.backupCount > 0:
            for i in range(self.backupCount - 1, 0, -1):
                sfn = self.rotation_filename("%s.%d" % (self.baseFilename, i))
                dfn = self.rotation_filename("%s.%d" % (self.baseFilename, i + 1))
                if os.path.exists(sfn):
                    if os.path.exists(dfn):
                        os.remove(dfn)
                    os.rename(sfn, dfn)
            dfn = self.rotation_filename(self.baseFilename + ".1")
            if os.path.exists(dfn):
                os.remove(dfn)
            self.rotate(self.baseFilename, dfn)
            os.chmod(dfn, stat.S_IRUSR)
        if not self.delay:
            self.stream = self._open()
            os.chmod(self.baseFilename, stat.S_IRUSR + stat.S_IWUSR)


LOGGER_PATH = os.getenv("LOGGING_FILE_DIR")
if not LOGGER_PATH:
    LOGGER_PATH = os.getenv("LOGGING_FILE_DIR")

if not os.path.exists(LOGGER_PATH):
    os.makedirs(LOGGER_PATH)

import logging.handlers

logging.handlers.IngestionRotatingFileHandler = IngestionRotatingFileHandler

log_dict = {
    'version': 1,
    'disable_existing_loggers': False,
    'root': {
        # handler中的level会覆盖掉这里的level
        'level': 'INFO',
        'handlers': ['server', 'console']
    },

    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'standard',
            'level': 'INFO',
        },
        'default': {
            'class': 'logging.handlers.IngestionRotatingFileHandler',
            'level': 'INFO',
            'filename': os.path.join(LOGGER_PATH, 'server.log'),
            'formatter': 'standard',
            'maxBytes': 1024 * 1024 * 50,  # 50 MB
            'backupCount': 5,
            'encoding': 'utf-8',
        },
        'server': {
            'class': 'logging.handlers.IngestionRotatingFileHandler',
            'level': 'INFO',
            'filename': os.path.join(LOGGER_PATH, 'server.log'),
            'formatter': 'standard',
            'maxBytes': 1024 * 1024 * 50,  # 50 MB
            'backupCount': 5,
            'encoding': 'utf-8',
        },
        'error': {
            'level': 'ERROR',
            'class': 'logging.handlers.IngestionRotatingFileHandler',
            'filename': os.path.join(LOGGER_PATH, 'error.log'),
            'formatter': 'standard',
            'maxBytes': 1024 * 1024 * 50,  # 50 MB
            'backupCount': 5,
            'encoding': 'utf-8',
        },
        'audit': {
            'level': 'INFO',
            'class': 'logging.handlers.IngestionRotatingFileHandler',
            'filename': os.path.join(LOGGER_PATH, 'audit.log'),
            'formatter': 'standard',
            'maxBytes': 1024 * 1024 * 50,  # 50 MB
            'backupCount': 5,
            'encoding': 'utf-8',
        },
        'scheduler': {
            'level': 'INFO',
            'class': 'logging.handlers.IngestionRotatingFileHandler',
            'filename': os.path.join(LOGGER_PATH, 'scheduler.log'),
            'formatter': 'standard',
            'maxBytes': 1024 * 1024 * 50,  # 50 MB
            'backupCount': 5,
            'encoding': 'utf-8',
        },
    },

    'formatters': {
        'standard': {
            'format': '[%(asctime)s][%(levelname)s][%(pathname)s:%(funcName)s:%(lineno)d]:%(message)s'
        }
    },
    'loggers': {
        'server': {
            'handlers': ['server'],
            'level': os.getenv('LOG_LEVEL', 'INFO'),
            #  是否向作用到root handle上
            'propagate': True
        },
        'error': {
            'handlers': ['error'],
            # 生产设置为ERROR
            'level': 'ERROR',
        },
        'audit': {
            'handlers': ['audit'],
            # 生产设置为INFO
            'level': os.getenv('LOG_LEVEL', 'INFO'),
            'propagate': False
        },
        'flask': {
            'handlers': ['console', 'server'],
            'level': 'INFO',
            'propagate': False
        },
        'scheduler': {
            'handlers': ['scheduler'],
            'level': 'INFO',
        }
    },
}

dictConfig(log_dict)

__logger_instances = {
    'server': logging.getLogger("server"),
    'error': logging.getLogger("error"),
    'flask': logging.getLogger('flask'),
    'audit': logging.getLogger('audit')
}


def getLogger(name):
    if name in __logger_instances:
        logger = __logger_instances[name]
    else:
        logger = __logger_instances['server']

    return logger

如果本地要打印日志,root handlers里要加上console,并且’propagate’为 True

数据库迁移

from flask_migrate import Migrate, MigrateCommand


def main():
    app = create_app()
    db.app = app
    manager = Manager(app)
    manager.add_command('db', MigrateCommand)
    manager.run()

     1、初始化migrate文件夹,仅第一次时需要执行。
         python3 __main__.py db init
      2、生成迁移记录
         python3 __main__.py db migrate
      3、提交更新至数据库
         python __main__.py db upgrade
      4、查看历史
         python3 __main__.py db history
         <base> ->  xxxx (head), empty message
      5、回退
         python sql_.py db downgrade 6af28765840d

项目目录初始化


import os
import sys
 

ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, ROOT_DIR)
SERVICE_DIR = os.path.dirname(os.path.realpath(__file__))

sys.path.append(SERVICE_DIR)

国际化

  1. 初始化,在命令行中进入到工程目录下,创建babel.cfg文件(要扫描的文件类型)。
    文件内容如下:

    [python: **.py]
    
    

    执行以下命令

    pybabel extract -F babel.cfg -o messages.pot .
    pybabel init -i messages.pot -d translations -l en
    

    执行命令后会在当前目录生成一个 translations 文件夹,翻译模板就在
    translations/zh/LC_MESSAGES/messages.po 文件里。

  2. 增加翻译词条操作说明

    1. 导入 from flask_babel import gettext as _

    2. 将需要翻译的文字用 _(“xxxx”) 表示

    3. 重新执行自动检索

      pybabel extract -F babel.cfg -o messages.pot .

      在当前目录生成messages.pot , 此时应该增加了 xxxx 词条。

    4. 执行更新

      pybabel update -i messages.pot -d translations

    5. translations/en/LC_MESSAGES/messages.po文件对应词条下增加翻译。

    6. 编译

      pybabel compile -d translations
      9、

【结合swagger 开发flask】
1、生成基础工程 generate server
http://editor.swagger.io/

2、参数类型
参考:https://www.breakyizhan.com/swagger/2952.html

  1. 在uri中携带的参数:如 datasets/{dataset_id}/status
    in: “path”
    参数获取:operationId 对应函数的 参数里 def xxx(dataset_id)
  2. 在url中携带的参数:如 xxuri?status=‘failed’
    in: “query”
    参数获取:operationId 对应函数的 参数里 def xxx(status)
  3. 在请求体里
    in: “body”
    request.json.get(‘dataset_id’)
  4. 请求体里,类型是form
    request.form
    request.files

【工程记录】
1、不能把绑定id的事情交给前端。
2、nginx 传输大文件 404,可能是权限不对
open() “xx/nginx/tmp/nginx_client_body/0000000009” failed (13: Permission denied)
chown -R xx /opt/huawei/openresty/nginx/tmp/
3、通过postman form 发送文件
应该把conten-type 去掉,用postman 默认生成的。
4、Mysql默认的字符检索策略:utf8_general_ci,表示不区分大小写;utf8_general_cs表示区分大小写,utf8_bin表示二进制比较,同样也区分大小写 。(注意:在Mysql5.6.10版本中,不支持utf8_genral_cs!!!!)

创建表时,直接设置表的collate属性为utf8_general_cs或者utf8_bin;如果已经创建表,则直接修改字段的Collation属性为utf8_general_cs或者utf8_bin。

5、nginx 转发
注意resolve问题
location /ixxdatasets/show/ {
proxy_set_header Host xxx.com;
proxy_pass https://xxx.com;

}

Nginx 报错 no resolver defined to resolve
起因是使用 ngxin + lua 链接外部机器

原因是 Nginx 0.6.18以后的版本中启用了一个resolver指令,在使用变量来构造某个server地址的时候一定要用resolver指令来指定DNS服务器的地址,所以解决这个问题的方法很简单:在nginx的配置文件中的http{}部分添加一行DNS地址即可
http {
resolver 8.8.8.8;
server {

}
}
如果机器支持 ipv6,可以去掉对它的支持,免得产生类似错误。

1
resolver 8.8.8.8 ipv6=off;

6、lua学习!!
lua 脚本
日志打印
ngx.log(ngx.ERR, “==url=” … url … “#####”)

ngx.var.arg与ngx.req.get_uri_args的区别 一个转码 一个不转码

    local uri_args = ngx.req.get_uri_args()
    local file_name = uri_args['file_name']
	local user_obs_path = uri_args['user_obs_path']
    local file_name = ngx.var.arg_file_name
	local user_obs_path = ngx.var.arg_user_obs_path

7、curl 命令 linux 下& 需要转义成&& 并用在url上加上双引号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值