前言

一般来说,解决sqlalchemy 连接多个库的最简单的方式是新建两个或多个db.session 相互没有关联,然后modle配置不同的db.session来连接,这样的话,relationship正常配置就行,不用特殊配置.

如果这样解决的话,也就不用看下面的配置了,下面是使用SQLALCHEMY_BINDS配置多个多个数据库并使用relationship.(个人建议最好不用relationship,很容易发生各种报错...)

# -*- coding:utf-8 -*-

__doc__ = "使用SQLALCHEMY_BINDS 就必须双份Model各自配置__bind_key__ ,同名库的读写分离与relationship配置的示例"

import flask

from flask_sqlalchemy import SQLAlchemy # Flask-SQLAlchemy 2.3.2

from datetime import datetime

from sqlalchemy.orm import backref, foreign # SQLAlchemy 1.3.1

db_name = 'db_name'

SQLALCHEMY_DATABASE_URL_READ = 'mysql://toto:toto123@111.111.111.111:3306/%s?charset=utf8' % db_name

SQLALCHEMY_DATABASE_URL_WRITE = 'mysql://toto1:toto123@222.222.222.222:3306/%s?charset=utf8' % db_name

app = flask.Flask(__name__)

app.config['DEBUG'] = True

app.config['SQLALCHEMY_BINDS'] = {

'read_db': SQLALCHEMY_DATABASE_URI_READ,

'write_db': SQLALCHEMY_DATABASE_URL_WRITE,

}

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

app.config['SQLALCHEMY_ECHO'] = True

db = SQLAlchemy(app)

class RDriver(db.Model):

__bind_key__ = 'read_db'

__tablename__ = 'driver'

# __table_args__ = {'schema': db_name} # __tablename__ 相同的,读库写库至少有一个要加,此示例是读库都不加,写库必须加

id = db.Column(db.Integer, primary_key=True, autoincrement=True)

fk_user_id = db.Column(db.Integer, db.ForeignKey("user.id"))

create_at = db.Column(db.TIMESTAMP, default=datetime.now)

class RUser(db.Model):

__bind_key__ = 'read_db'

__tablename__ = 'user'

# __table_args__ = {'schema': db_name}

id = db.Column(db.Integer, primary_key=True, autoincrement=True)

username = db.Column(db.String(32), index=True, unique=True)

email = db.Column(db.String(32))

create_time = db.Column(db.TIMESTAMP, default=datetime.now)

update_time = db.Column(db.TIMESTAMP, default=datetime.now)

# 如下的五种方式都是可以的

# driver_fk = db.relationship("RDriver", foreign_keys='RDriver.fk_user_id')

# driver_fk = db.relationship("RDriver", primaryjoin=lambda: RDriver.fk_user_id == RUser.id, viewonly=True)

# driver_fk = db.relationship("RDriver", primaryjoin=RDriver.fk_user_id == id)

fk_driver = db.relationship("RDriver", primaryjoin='RDriver.fk_user_id == RUser.id')

# driver_fk = db.relationship("RDriver", backref=db.backref('user', lazy=True),

# primaryjoin=lambda: RDriver.fk_user_id == RUser.id, viewonly=True)

####################################################上面为同名读库,下面为同名写库###########################################

class WDriver(db.Model):

__bind_key__ = 'write_db'

__tablename__ = 'driver'

__table_args__ = {'schema': db_name, 'extend_existing': True} # 这个配置很关键,同名写库都要配置

# __table_args__ = {'extend_existing': True}

# 这样配置的话会报错: sqlalchemy.exc.ArgumentError: Could not locate any relevant foreign

# key columns for primary join condition 'driver.fk_user_id = "user".id' on relationship RUser.fk_driver.

# Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint,

# or are annotated in the join condition with the foreign() annotation.

id = db.Column(db.Integer, primary_key=True, autoincrement=True)

fk_user_id = db.Column(db.Integer, db.ForeignKey("%s.user.id" % db_name)) # db_name.user.id很关键,必须指定用的数据库名

create_at = db.Column(db.TIMESTAMP, default=datetime.now)

class WUser(db.Model):

__bind_key__ = 'write_db'

__tablename__ = 'user'

__table_args__ = {'schema': db_name, 'extend_existing': True} # 这个配置很关键,同名写库都要配置

# __table_args__ = {'extend_existing': True} # 这个配置很关键,同名写库都要配置

id = db.Column(db.Integer, primary_key=True, autoincrement=True)

username = db.Column(db.String(32), index=True, unique=True)

email = db.Column(db.String(32))

create_time = db.Column(db.TIMESTAMP, default=datetime.now)

update_time = db.Column(db.TIMESTAMP, default=datetime.now)

# 以下五种方式都是可以的

# fk_driver = db.relationship("WDriver", foreign_keys='WDriver.fk_user_id', uselist=False)

# fk_driver = db.relationship("WDriver", primaryjoin=lambda: WDriver.fk_user_id == WUser.id)

fk_driver = db.relationship("WDriver", primaryjoin=WDriver.fk_user_id == id)

# fk_driver = db.relationship("WDriver", primaryjoin='WDriver.fk_user_id == WUser.id')

# fk_driver = db.relationship("WDriver", backref=db.backref('test.user', lazy=True),

# primaryjoin=lambda: WDriver.fk_user_id == WUser.id)

r_user_obj = RUser.query.filter_by().first()

print("r_user_obj:", r_user_obj)

print("r_user_obj.driver_fk:", r_user_obj.fk_driver)

""" 打印出来的sql

SELECT user.id AS user_id, user.username AS user_username, user.email AS user_email, user.create_time AS user_create_time, user.update_time AS user_update_time

FROM user

LIMIT 1

SELECT driver.id AS driver_id, driver.fk_user_id AS driver_fk_user_id, driver.create_at AS driver_create_at

FROM driver

WHERE driver.fk_user_id = %s

"""

w_user_obj = WUser.query.filter_by(id=2188).first()

print("w_user_obj:", w_user_obj)

print("w_user_obj.driver_fk:", w_user_obj.fk_driver)

""" 打印出来的sql,可以看出多了数据库名

SELECT db_name.user.id AS db_name_user_id, db_name.user.username AS db_name_user_username, db_name.user.email AS db_name_user_email, db_name.user.create_time AS db_name_user_create_time, db_name.user.update_time AS db_name_user_update_time

FROM db_name.user

WHERE db_name.user.id = %s

LIMIT %s

SELECT db_name.driver.id AS db_name_driver_id, db_name.driver.fk_user_id AS db_name_driver_fk_user_id, db_name.driver.create_at AS db_name_driver_create_at

FROM db_name.driver

WHERE db_name.driver.fk_user_id = %s

"""

参考文档:

* https://docs.sqlalchemy.org/en/13/orm/relationship_api.html # 值得细看

* https://www.osgeo.cn/sqlalchemy/orm/relationship_api.html # 同上,中文

* https://www.cnblogs.com/srd945/p/9851227.html

* extend_existing: (False)当表已经存在于元数据中时,如果元数据中存在与column_list中的列同名的列,column_list中同名的列会替换掉元数据中已经有的列

* useexisting已被废弃, 新版本使用extend_existing

总结

关系配置参数真的很多,如下,很容易就会出错,需要多读读官方文档,还有就是建立modle时候尽量简洁,风格统一,不要在数据库层建立外键.

sqlalchemy.orm.relationship(argument, secondary=None, primaryjoin=None, secondaryjoin=None, foreign_keys=None,

uselist=None, order_by=False, backref=None, back_populates=None, post_update=False, cascade=False, extension=None,

viewonly=False, lazy='select', collection_class=None, passive_deletes=False, passive_updates=True, remote_side=None,

enable_typechecks=True, join_depth=None, comparator_factory=None, single_parent=False, innerjoin=False,

distinct_target_key=None, doc=None, active_history=False, cascade_backrefs=True, load_on_pending=False,

bake_queries=True, _local_remote_pairs=None, query_class=None, info=None, omit_join=None)

题外话

配置多连接数据库用户访问权限需要提前配置好,我就遇到个坑:

我连接了ip为111.111.111.111的数据库,使用mysql命令好都能很好的正常访问到数据,但是使用SQLALCHEMY_BINDS访问后就报错 :

OperationalError: (_mysql_exceptions.OperationalError) (1142, "SELECT command denied to user 'toto'@'125.121.34.123' for table 'user'") [SQL: u'SELECT test2.user.com_name AS test2_user_com_name, test2.user.com_no AS test2_user_com_no \nFROM test2.user \nWHERE test2.user.id = %s \n LIMIT %s'] [parameters: (3385, 1)]

其实还是自己没有检查清楚,toto用户我只给了test库的权限,而test2库的权限没给,才会造成这样的报错(125.121.34.123别被这个ip吸引了注意力,我上次遇到这样的问题是配置中SQLALCHEMY_DATABASE_URL的连接有点错误,导致前后看到的ip不一样,这次连接中的111.111.111.111和报错中的125.121.34.123不一样是因为ip不在同一网段,125.121.34.123就是111.111.111.111经过多段路由连接过来的后分配到的ip)

mysql toto@111.111.111.111:test> SELECT test2.user.username AS test_user_username, test2.user.id AS test_ship

pers_id FROM test2.user WHERE test2.user.id = 3385 LIMIT 1;

(1142, "SELECT command denied to user 'toto'@'125.121.34.123' for table 'user'")

mysql toto@111.111.111.111:test> SELECT test.user.username AS test_user_username, test.user.id AS

test_user_id FROM test.user WHERE test.user.id = 3385 LIMIT 1;

+--------------------------+-----------------------+

| test_user_username | test_user_id |

+--------------------------+-----------------------+

| XXXXXXX | 123 |

+--------------------------+-----------------------+

mysql表的relationship_sqlalchemy 配置多连接读写库后的relationship设置相关推荐

  1. pyspark —— spark dataframe 从hdfs读写文件:按照指定文件格式读写文件(读写csv、json、text文件,读取hive表,读取MySQL表)、按照指定分隔符读写文件

    spark有3种数据结构--RDD.DataFrame.DataSet.这里展示的文件读写方式,都是针对dataFrame数据结构的,也就是文件读进来之后,是一个spark dataFrame. 0. ...

  2. 全网最全的mysql表的关联查询(内连接,外连接)

    Mysql 多表连接查询 inner join 和 outer join 的使用 JOIN的含义就如英文单词"join"一样,连接两张表,大致分为 内连接,外连接,右连接,左连接, ...

  3. quartz mysql 表 集群配置_Spring整合实战丨Quartz 集群配置

    Quartz说明 1.quartz可以通过jdbc直连连接到MYSQL数据库,读取配置在数据库里的job初始化信息,并且把job通过java序列化到数据库里,这样就使得每个job信息得到了持久化,即使 ...

  4. yii2 pdo mysql 乱码_YII2.0使用PDO连接Oracle库查询结果中文显示乱码问题

    自己来解答,直接上代码. 创建链接测试用小程序如下: namespace app\models; use Yii; use yii\db\ActiveRecord; use \yii\db\Conne ...

  5. php7.3 mysql gd支持_配置PHP对gd库的支持

    搭建zabbix的时候遇到有对PHP的需求检测,发现没有对gd的支持,记录下... GD库是php处理图形的扩展库,它提供了一系列用来处理图片的API,使用GD库可以处理图片,或者生成图片,也可以给图 ...

  6. mysql 表如何分区数据恢复_硬盘重新分区后数据如何恢复?轻松一招搞定!

    位置:数据恢复的知识与技巧 - 硬盘重新分区后数据恢复 硬盘重新分区后数据恢复 硬盘重新分区的过程会将原有分区和数据抹去并建立新的分区,那么重新分区后丢失的分区及数据还能恢复吗?今天小编就和大家一起研 ...

  7. windows下mysql8.x配置远程连接

    文章目录 1. 现象 2. 登录mysql 3.先查看下当前的用户,具有什么权限 4.创建新的用户之后再查权限 5. 赋予权限 6.刷新权限,然后就可远程访问了 1. 现象 Host '192.168 ...

  8. Mysql主从配置,实现读写分离

    大型网站为了软解大量的并发访问,除了在网站实现分布式负载均衡,远远不够.到了数据业务层.数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库连接操作,数据库必然会崩溃,数据丢 ...

  9. 配置mysql读写主从_Mysql主从配置,实现读写分离

    MySQL数据库监控功能作为SUM服务器监控软件的一项基本功能,从SUM服务器监控软件发布开始就一直作为其核心功能之一,大量用户环境中正式使用,具有稳定.易用.指标齐全等特点. 通过SUM可以快速监控 ...

最新文章

  1. mysql 主从单库单表同步 binlog-do-db replicate-do-db
  2. Java核心技术及面试指南 异常部分的面试题归纳以及答案
  3. (转)实现自己的http server
  4. PUT上传POC--Put2Poc.py
  5. C语言与汇编“硬在哪里”——什么是面向硬件?
  6. 使用.NET Mobile API即51Degrees.mobi检测User-Agent
  7. 第五章平稳过程(1)
  8. 教育类产品 竞品分析
  9. Paxos 算法详解
  10. MQ 消息队列时如何确保消息不丢失
  11. 仿网易云音乐的YY音乐微信小程序源码
  12. 美国黄岩超级计算机,飓风预测 揭秘最快气候研究“黄石”超算
  13. isNaN、Number.isNaN、isFinite、Number.isFinite
  14. SecureCRT win7 安装破解使用
  15. 【单片机笔记】详解如何用廉价NTC电阻准确高效的测量温度(附源码)
  16. idog copy from,
  17. 计算机特点及应用领域阐述,计算机图像识别技术的应用及细节问题阐述与分析...
  18. QQShow?你也可以做!
  19. innodb的索引下推
  20. 立根铸魂 崛起数智时代 欧拉部署超300万套

热门文章

  1. sql server高可用_SQL Server始终在线可用性组采访问题与解答
  2. 如何自学并且系统学习计算机网络?(知乎问答)
  3. 文件上传中的临时上传路径问题
  4. linux 查看磁盘空间大小命令
  5. 统计每天的数据 无数据也要显示日期程序解决方法
  6. 常见压缩/解压缩及打包命令
  7. 1004. Counting Leaves (30)
  8. C#控件的闪烁问题解决方法总结
  9. 现在的孩子太厉害了阿(老朱语:长江后浪推前浪)
  10. html 图片隐藏 一部分,如何在HTML / CSS中仅显示图像的一部分?