记录一次经历:使用flask_sqlalchemy集成flask造成循环导入问题

news2024/11/15 8:20:15

前言:

        工作需求,写一个接口,用Python来编写,我首先想到用flask小型框架来支撑,配置sqlalchemy来实现,但是在实现的过程中,发生循环导入问题

        我想到用蓝图来解决此问题,但是仍然会出死循环的问题,网上找了很多资料,许多都是轻描淡写,可能不是很明白,为了能够帮助更多踩坑的人,分享一下自己的经历!!

了解导入循环的机制

报错代码:

        cannot import name '......' from partially initialized module '.....'

Python的 死循环机制:

        当模块之间相互导入时,两个或多个模块之间相互依赖,它们通过import语句相互引用对方,而没有一个明确的导入顺序或者依赖管理来打破这种循环,形成了一个闭环,当尝试从任何一个模块中导入或执行函数时,Python 解释器将陷入无限循环的导入过程中

比如:A导入B,B导入A  

模块A:module_a.py

import module_b  
  
def func_a():  
    print("Function A")  
    module_b.func_b()

模块B:module_b.py

import module_a  
  
def func_b():  
    print("Function B")  
    module_a.func_a()

比如:A导入C,B导入A,C导入B

模块A:test1.py

import test3


def t1():
    print('test1')
    test3.t3()

t1()

模块B:test2.py

import test1


def t2():
    test1.t1()
    print("test2")

模块C:test3.py

import test2


def t3():
    test2.t2()
    print("test3")

解决sqlalchemy集成flask导入死循环的问题

        言归正传,要解决问题,要注意db实例化的位置,app导入的位置,蓝图导入的顺序!!!

解决方案:

   1. 在cab_api_test/views/__init__.py 文件下,实例化db,函数封装app   

   3. 导入蓝图时,如果蓝图有引用db,位置需要在db的下方  
   2. 在models中,main语句下,导入app,如果在全局还是会发生循环问题

        可能说的有些不清不楚,还是通过代码体现,就能明白啦,每步骤关键都有注释,注意好导入的顺序,和导入的位置,主要是app和db的位置,和模型创建用到的app导入问题!!

目录结构:

cab_api_test
├── app.py
├── gunicorn_config.py
├── logs
│   ├── cab_api_access.log
│   └── cab_api_error.log
├── requirements.txt
└── views
    ├── __init__.py
    ├── __pycache__
    │   ├── __init__.py
    │   ├── cab_api.py
    │   ├── filed_heck.py
    │   ├── logger.py
    │   ├── models.py
    │   └── session.py

cab_api_test/views/__init__.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

# 1. 实例化db时 要注意导入蓝图的顺序!!!
# 2. 如果蓝图中有引用db,在这导入的蓝图必须在db的下面,不然还是会报错
# 3. 我是用的蓝图是 cab_api.py
from views.cab_api import cab_api

host = '127.0.0.1'
port = 3306
user = 'root'
password = 'wq123456'
database = 'gf_test'


def create_app():
    app = Flask(__name__)
    app.config['DEBUG'] = True
    # sqlalchemy 连接池配置
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config[
        'SQLALCHEMY_DATABASE_URI'] = f'mysql+pymysql://{user}:{password}@{host}:{port}/{database}?charset=utf8mb4&autocommit=true'
    app.config['SQLALCHEMY_POOL_SIZE'] = 10
    app.config['SQLALCHEMY_POOL_TIMEOUT'] = 10
    app.config['SQLALCHEMY_MAX_OVERFLOW'] = 5
    app.config['SQLALCHEMY_POOL_PRE_PING'] = True
    app.config['SQLALCHEMY_ECHO'] = False

    # 把app对象初始化到db中
    db.init_app(app=app)

    app.register_blueprint(cab_api, url_prefix='/api')
    return app


cab_api_test/views/models.py

import datetime
from uuid import uuid4
from sqlalchemy import Column, String, Text, DateTime

# 这个db是全新的SQLAlchemy对象
from views import db


class Cab_review(db.Model):
    __tablename__ = 'cab_review'  # 表名
    # 写字段
    uuid = Column(String(100), nullable=False, primary_key=True, default=str(uuid4()).replace('-', ''))
    cab_number = Column(String(64), nullable=True, comment='工单号')
    review_item = Column(String(100), comment='项目名')  # String(32) == varchar(32)
    review_info = Column(Text, comment='项目信息')
    review_link = Column(String(200), nullable=True, comment='项目链接')
    caller_id = Column(String(100), nullable=True, comment='调用方标识ID')
    caller_ip = Column(String(100), nullable=True, comment='调用方IP')
    updated_time = Column(DateTime, default=datetime.datetime.now, nullable=False, comment='更新时间')
    test_number = Column(String(64), nullable=True, comment='测试号')


if __name__ == '__main__':  # 导入时不执行下面的语句,只有执行当前文件的时候才会执行下面的代码

    # 注意要在main下面导入create_app,如果在全局导入,还是会发生循环导入的问题
    from views import create_app

    app = create_app()

    # 先清空再创建
    db.drop_all()
    db.create_all(app=app)


# 做一个简单的封装 db,使用with 语句
class Session:
    def __init__(self):
        self.db = None

    def __enter__(self):
        self.db = db.session
        return self.db

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.db:
            self.db.close()

 cab_api_test/views/cab_api.py

from flask import jsonify, request
import json, datetime
from uuid import uuid4
from views.logger import flask_logger
from views.filed_check import field_check
from flask import Blueprint
# Session 是我封装好的 db
from views.models import Cab_review, Session

cab_api = Blueprint('cab_api', __name__)


@cab_api.route('/', methods=['POST', ])
def main():
    # 传入的参数
    data = request.get_json()
    cno_test = data.get('cno_test')
    review_pro = data.get('review_pro')
    review_result = data.get('review_result')
    review_advice = data.get('review_advice')

    flask_logger.info(f'请求体:{data}')

    data['cno'] = '已废弃' if not data.get('cno') else data.get('cno')
    data['caller_ip'] = request.remote_addr
    data['url'] = data.get('url') if data.get('url') else ''
    data['caller_id'] = data.get('caller_id') if data.get('caller_id') else '调用方未提供caller_id'

    # 校验字段
    ok, msg = field_check(cno_test, review_pro, review_result, review_advice)
    if not ok:
        flask_logger.info(
            '-------------------------------------------------------------------------------------------------------')
        return jsonify(msg)

    # 查询cab_review中的数据
    with Session() as session:
        select_data = session.query(Cab_review).filter_by(test_number=cno_test,
                                                          review_item=review_pro).all()  # [queryset,queryset],数据为空则 []
        session.begin()  # 开启事务
        if not select_data:
            try:
                cab_review = Cab_review(uuid=str(uuid4()).replace('-', ''),
                                        cab_number=data.get('cno'),
                                        review_item=data.get('review_pro').strip(),
                                        review_info=json.dumps(
                                            {'review_result': data.get('review_result'),
                                             'review_advice': data.get('review_advice')},
                                            ensure_ascii=False),
                                        review_link=data.get('url'),
                                        caller_id=data.get('caller_id'),
                                        caller_ip=data.get('caller_ip'),
                                        test_number=data.get('cno_test'))
                session.add(cab_review)
                session.commit()
                flask_logger.info('数据插入成功')
                msg = '请求成功,数据已添加或更新'
                is_success = True
            except Exception as e:
                session.rollback()
                flask_logger.error(f'添加失败:{e}', exc_info=False)
                msg = '数据添加或更新失败,请联系管理员'
                is_success = False
        else:
            try:
                session.query(Cab_review).filter_by(test_number=data.get('cno_test'),
                                                    review_item=data.get('review_pro').strip()).update(
                    {'review_info': json.dumps(
                        {'review_result': data.get('review_result'), 'review_advice': data.get('review_advice')},
                        ensure_ascii=False), 'review_link': data.get('url'),
                        'caller_id': data.get('caller_id'),
                        'caller_ip': data.get('caller_ip'), 'updated_time': datetime.datetime.now()})

                session.commit()
                flask_logger.info('数据更新成功')
                msg = '请求成功,数据已添加或更新'
                is_success = True
            except Exception as e:
                session.rollback()
                flask_logger.error(f'更新失败:{e}', exc_info=False)
                msg = '数据添加或更新失败,请联系管理员'
                is_success = False

    review_name = ['评审项:' + review_pro, '评审结果:' + review_result, '评审意见:' + review_advice,
                   '评审链接:' + data.get('url')]
    flask_logger.info(
        '-------------------------------------------------------------------------------------------------------')
    return {'code': 200, 'success': is_success, 'msg': msg, 'data': '\n'.join(review_name)}


@cab_api.route('/', methods=['GET', ])
def health_check():
    return 'ok'

这样就不会在发生导入死循环的问题啦!!! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2070502.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UI测试使用webdriver-manager免安装浏览器驱动

引言: selenium传统的方式是下载浏览器对应的driver(驱动),放到本地的指定位置,然后写代码加载这个driver(驱动)再执行相应的操作。 弊端: 传统方法存在两个麻烦的地方: 1.需要下…

安全面试常见问题任意文件下载

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 1.1 任意文件下…

Git的使用教程及常用语法03

七.如何从版本库中删除文件 第一种方式:直接在工作区删除文件,然后提交 rm ffile1.txt (注意:这个不是git命令,而是linux命令) 看到状态发现,文件file1.txt已经被删除,提示需要提交到暂存区。 因为我们只…

蓝牙对象交换协议(OBEX) - 概念介绍

零.声明 本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下: 第一篇:蓝牙综合介绍 ,主要介绍蓝牙的一些概念,产生背景,发展轨迹,市面蓝牙介绍,以及蓝牙开发板介绍。 第二篇:Trans…

SpringBoot集成kafka-监听器注解

SpringBoot集成kafka-监听器注解 1、application.yml2、生产者3、消费者4、测试类5、测试 1、application.yml #自定义配置 kafka:topic:name: helloTopicconsumer:group: helloGroup2、生产者 package com.power.producer;import com.power.model.User; import com.power.uti…

Windows系统上进行项目管理工具VisualSVN Server服务端的保姆级安装教程与配置和SVN客户端保姆级安装教程和使用

一、VisualSVN Server简介 Subversion Server for Windows | VisualSVN ServerGet an easy to use Subversion (SVN) server for Windows. It works out-of-the-box and is suitable both for small business and enterprises. Available for free!https://www.visualsvn.com/…

4.Redis单线程和多线程

1.Redis的单线程 Redis的单线程主要是指Redis的网络IO和键值对读写是由一个线程完成的,Redis在处理客户端的请求时包括获取(Socket读)、解析、执行、内容返回(Socket写)等都由一个顺序串行的主线程处理,这…

Linux 下命令行参数和环境变量

Linux 下命令行参数和环境变量 命令行参数为什么要有命令行参数谁可以做到结论 环境变量一些现象查看环境变量添加环境变量添加内存级环境变量永久有效 其他环境变量HOMEPWDSHELLHISTSIZE 自定义环境变量定义取消 本地变量整体理解环境变量环境变量的组织方式Linux 代码获取环境…

SpringBoot集成kafka接收对象消息

SpringBoot集成kafka接收对象消息 1、生产者2、消费者3、工具类4、消息实体对象5、配置文件6、启动类7、测试类8、测试结果 1、生产者 package com.power.producer;import com.power.model.User; import com.power.util.JSONUtils; import org.springframework.kafka.core.Kaf…

UEStudio V24 中文授权版

UEStudio是一款集成开发环境(IDE)软件,主要用于编写和编辑各种类型的代码,包括C/C、Java、HTML、PHP、Perl、Python等。 软件截图: 使用说明: 解压后,双击start_UEStudio.bat来运行软件 下载地…

【计算机组成原理】计算机系统概述<1>

学习目标: 掌握计算机组成原理的基础知识巩固 例如: 信息化世界的组成 计算机系统概述 计算机硬件基本组成 各个硬件的工作原理 计算机软件 计算机系统的多层次结构 计算机系统的工作原理 计算机性能指标 学习内容: 1.0、初入计算机组成原…

Apollo9.0 PNC源码学习之Planning模块—— Lattice规划(七):横纵向运动轨迹的优选

参考文章: (1)Apollo6.0代码Lattice算法详解——Part 7: 获得最优轨迹 (2)Lattice算法详解 0 前言 // 优选出cost最小的trajectory// 7. always get the best pair of trajectories to combine; return the first// collision-free trajectory.size_t constraint_failure…

Latent-OFER:使用潜在向量进行检测、屏蔽和重建,以实现遮挡的面部表情识别

论文:Latent-OFER: Detect, Mask, and Reconstruct with Latent Vectors for Occluded Facial Expression Recognition 摘要:所提出的方法Latent-OFER可以检测遮挡,将面部被遮挡的部分恢复为未被遮挡的部分,并识别它们&#xff0…

【Java自动化学习】Web自动化

一、环境安装 环境搭建安装见此博客文章链接:https://blog.csdn.net/qq_73471456/article/details/130836494 二、元素定位、等待方式 见此之前的博客文章:selenium操作使用方式 三、下拉框定位 四、iframe 切换元素定位 注意事项:连续定…

数学排列组合

我突然想发一篇文章(别问我为什么[doge]) 排列组合大家都听过吧,今天的主角就是排列组合。 废话不多说,直接开始 先来看几道题目: :由1,2,3,4组成不同的三位数有几种? :有四个人,每两个人都要握手一次,要握…

【秋招笔试】8.24美团秋招(算法岗)-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收…

[JAVA] 什么是Java线程同步机制?

在单线程程序中,每次只能做一件事情,后面的事情需要等待前面的事情完成后才可以进行,如果使用多线程程序,就会发生两个线程抢占资源的问题,所以在多线程编程中,需要防止这些资源访问的冲突,Java…

LED显示屏原理及其系统组成

随着城市化进程的加快,LED显示屏的需求在各个行业中迅速增长。无论是用于广告宣传、信息发布,还是场馆显示,LED显示屏都扮演着重要的角色。然而,对于很多人来说,LED显示屏的工作原理及其系统组成可能并不为熟知。本文将…

589. N 叉树的前序遍历(递归法)

目录 一:题目: 二:代码: 三:结果: 一:题目: 给定一个 n 叉树的根节点 root ,返回 其节点值的 前序遍历 。 n 叉树 在输入中按层序遍历进行序列化表示,每…

Java JNA调用C函数常见问题及解决方法

目录 1 undefined symbol:xxx2 Java映射C数组乱码3 Java使用String接收不到C函数返回的char*4 Unable to load DLL xxx.dll5 java.lang.UnsatisfiedLinkError: %1 不是有效的 Win32 应用程序6 无效的ELF头7 Structure array elements must use contiguous memory8 j…