attention
использоватьsession.queryзаменятьmodel.query,model.queryДанные базы данных должны быть сопоставлены с моделью, и используются данные базы данных, которые должны быть изменены на лету.model.queryнельзя напрямую спрашивать
session.add()После выполнения оператор SQL будет добавлен к ожидающим и использован снова.session.queryБудет принудительно выполнять ожидающий SQL, а затем выполнять операцию запроса иmodel.querySQL в ожидании не будет применяться. (Конкретные принципы и методы реализации подлежат изучению.)
Журнал выглядит следующим образом:
>>> from app import db
>>> session = db.session
>>> from app import User
>>> user = User(name='test1', pwd='test1')
>>> session.add(user)
>>> session.query(User).filter()
>>> session.query(User).filter().all()
2022-01-06 16:57:54,286 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2022-01-06 16:57:54,287 INFO sqlalchemy.engine.Engine INSERT INTO user (name, pwd, addtime) VALUES (%(name)s, %(pwd)s, %(addtime)s)
2022-01-06 16:57:54,287 INFO sqlalchemy.engine.Engine [generated in 0.00019s] {'name': 'test1', 'pwd': 'test1', 'addtime': None}
2022-01-06 16:57:54,292 INFO sqlalchemy.engine.Engine SELECT user.id AS user_id, user.name AS user_name, user.pwd AS user_pwd, user.addtime AS user_addtime
FROM user
2022-01-06 16:57:54,292 INFO sqlalchemy.engine.Engine [generated in 0.00038s] {}
session
Сначала создайте объект db, этот объект имеет всю инкапсуляцию базы данных в flask_sqlalchemy.
from flask import Flask
from flask_sqlalchemy imort SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app, engine_options={'pool_recycle': 3600, 'pool_size': 20})
# session = db.session
Flask_sqlalchemy инкапсулирует многие операции SQLAlchemy. Вы можете напрямую использовать конфигурацию приложения в flask для создания соединений с базой данных. Вы можете использовать собственные методы SQLlchemy для создания соединений с базой данных. Механизмом sqlalchemy по умолчанию является MySQLdb.pip install mysql-python, но эта библиотека больше не поддерживает python3, она появится при установке в среде python3ModuleNotFoundError: No module named 'ConfigParser'ошибка, потому что python3 будетConfigParser.pyфайл изменен наconfigparser.py, скопируйте файл, то есть модуль, какConfigParser.pyможет решить проблему.
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session
# 数据库+引擎://用户:密码@地址:端口/库?charset=utf8
mysql_uri = "mysql+pymysql://root:root@127.0.0.1:3306/SQLAlchemyTest?charset=utf8"
# paramstyle in ["qmark", "numeric", "named", "format" or "pyformat"]
engine = create_engine(
mysql_uri,
paramstyle=None,
echo=True,
echo_pool=True,
pool_pre_ping=True, # 悲观方式,每次执行语句先执行select语句实现ping的功能,可以在执行失败后重试
)
session_caller = sessionmaker(bind=engine, autoflush=True, autocommit=False, expire_on_commit=True, info=None)
session = session_caller()
# session = scoped_session(session_factory=Session_caller, scopefunc=None)
create_engineПринятые параметрыparamstyleявляется типом стиля параметра по умолчанию для python, вPEP249(paramstyle)подробно в
Обычно в качестве механизма sqlalchemy используется pymysql, но в случае без установления соединения
paramstyleНаступил на яму sqlalchemy и pymysql, pymysql находится в__init__.pyУказанный параметр ставится какpyformat, поэтому, если вы хотите указатьparamstyleЭтот параметр должен быть указан какNoneилиpyformat, иначе произойдет ошибка.В режиме подключения работает нормально.
Если транзакция не задействована, рекомендуется режим соединения. то есть через
engine.connect()Создать соединение, выполнить SQL
использоватьnamed
# 使用named
engine = create_engine(mysql_uri, paramstyle='named')
session = sessionmaker(engine)()
sql = 'select * from dev_user where id={}'
session.execute(sql.format(':user_id'), {'user_id': 100}).fetchone()
# sqlalchemy 并没有将:user_id格式化,而是直接传给了pymysql,导致出错
# ProgrammingError: (pymysql.err.ProgrammingError) (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ':user_id' at line 1")
# [SQL: select * from dev_user where id=:user_id]
# [parameters: {'user_id': 100}]
использоватьpyformat
# 使用pyformat
engine.paramstyle = 'pyformat'
session.execute(sql.format('%(user_id)s'), {'user_id': 100}).fetchone()
# sqlalchemy 没有获取到params参数
# ProgrammingError: (pymysql.err.ProgrammingError) (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%(user_id)s' at line 1")
# [SQL: select * from dev_user where id=%%(user_id)s]
# 而使用named风格的参数则没问题
session.execute(sql.format(':user_id'), {'user_id': 100}).fetchone()
# SQL:
# select * from dev_user where id=%(user_id)s
# {'user_id': 101}
Существует три способа использования sqlalchemy:
- raw sql
- sql expression
- ORM
Первые два становятся основными методами,УведомлениеЕсли это sqlalchemy+cx_oracle, вам нужно отключить пул соединений, иначе будут исключения.Метод заключается в том, чтобы установить sqlalchemy.poolclass в sqlalchemy.pool.NullPool
поддержка транзакций
Создайте соединение, чтобы открыть транзакцию, и создайте точку сохранения для данных, которые добавляются без фиксации.
db.session.add(u1)
# 创建一个save point
db.session.begin_nested()
db.session.add(u2)
db.session.rollback()
# 只回退u2,不回退u1
db.session.commit()
Ниже приведена демонстрация модели базы данных.
class User(db.Model):
__tablename__ = "dev_user"
id = db.Column(INTEGER(unsigned=True), primary_key=True)
username = db.Column(db.String(80), unique=True, doc="用户名", comment="用户id")
email = db.Column(db.String(120), unique=True, doc="用户邮箱", comment="用户邮箱")
is_active = db.Column(db.Boolean, default=False, doc="已激活", comment="是否已激活")
phone = db.Column(db.String(20), index=True, doc="用户手机号", comment="用户手机号")
age = db.Column(INTEGER(), doc="用户年龄", comment="用户年龄")
def __repr__(self):
return f"<User {self.username!r}>"
class Blog(db.Model):
__tablename__ = "blog"
id = db.Column(INTEGER(unsigned=True), primary_key=True)
content = db.Column(db.Text, comment="博客内容")
user_id = db.Column(INTEGER(unsigned=True), db.ForeignKey("dev_user.id"), index=True,
doc="用户id", comment="用户id")
class Comment(db.Model):
__tablename__ = "comment"
id = db.Column(INTEGER(unsigned=True), primary_key=True)
comment = db.Column(db.String(150), doc="用户评论", comment="用户评论")
user_id = db.Column(INTEGER(unsigned=True), index=True, doc"用户id", comment="用户id")
class UserInfo(db.Model):
__tablename__ = "user_info"
id = db.Column(INTEGER(unsigned=True), primary_key=True)
user_id = db.Column(INTEGER(unsigned=True), db.ForeignKey("dev_user.id"),
index=True, doc="用户id", comment="用户id",)
user = db.relationship("User", backref="user_info", uselist=False)
company = db.Column(db.String(100), unique=True, doc="公司简称", comment="公司简称")
Поддержка нескольких баз данных
flask_sqlalchemy использует концепцию привязки для привязки нескольких баз данных.
# 在配置config类中可以使用
class Config(object):
SQLALCHEMY_DATABASE_URI = 'postgres://localhost/main'
SQLALCHEMY_BINDS = {
'users': 'mysqldb://localhost/users',
'appmeta': 'sqlite:////path/to/appmeta.db'
}
# 在app.config属性的设置中可以使用
app.config["SQLALCHEMY_DATABASE_URI"] = 'postgres://localhost/main'
app.config["SQLALCHEMY_BINDS"] = {
'users': 'mysqldb://localhost/users',
'appmeta': 'sqlite:////path/to/appmeta.db'
}
В flask_sqlalchemy есть много пакетов с параметрами привязки, которые могут указывать один из нескольких параметров базы данных, например
db.session.execute(sql, bind='user')
Разница в SQL:
-- 不使用bind
SELECT user.username AS username_1 FROM user WHERE user.id = 100;
-- 使用bind
SELECT test.user.username AS username_1 FROM test.user WHERE test.user.id = 100;
-- 表名前缀为数据库名
Использование поддержки нескольких баз данных в определении базы данных
class User(db.Model):
__tablename__ = "user"
# 指定`__bind_key__`在执行sql语句时会直接选择指定的数据库
__bind_key__ = 'users'
# 或者使用__table_args__
__table_args__ = {
'schema': "users", # 指定bind
'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8mb4',
}
# __table_args__也可以使用元组
# __table_args__ = (
# db.UniqueConstraint("username", "id", name="uix_username_id"),
# db.Index("ix_username", "username")
# )
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
# sqlalchemy的多态特性
__mapper_args__ = {
"order_by": username.desc()
}
# 或者使用创建对象方式创建表模型
user_favorites = db.Table('user_favorites',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('message_id', db.Integer, db.ForeignKey('message.id')),
info={'bind_key': 'users'}
)
Базовое использование (CRUD)
Простой запрос
- Добавить к
Используйте db.session для добавления данных
user = User(username='Jack', email='jack@email.com')
db.session.add(user)
db.session.commit() # 手动commit
user = User(username='Vic', email='vic@email.com')
info = UserInfo(user=user, company='Google')
# 添加多个对象
db.session.add_all([user, info])
db,session.commit()
- удалять
удалить метод объекта запроса
Метод обновления и удаления принимает дополнительный параметрsynchronize_session=evaluate,synchronize_sessionДоступны три значения:
-
False: Обновите данные базы данных, не обновляйте данные, запрошенные в сеансе. -
fetch: обновить данные базы данных и снова прочитать данные из базы данных. -
evaluate: Параметр по умолчанию, если данные в сеансе не соответствуют данным в базе данных, сеанс не может определить, как с ним поступить, и он выкинетInvalidRequestErrorаномальный
rows = db.session.query(User).filter(User.id == 101).delete(synchronize_session='evaluate')
Однако, если вы удалите данные, связанные с внешним ключом напрямую, будет сообщено об ошибке, но вы можете использоватьsession.delete()Метод может обрабатывать конфигурацию внешнего ключа отношения.Если настроено cascade='all,delete-orphan', данные из таблицы будут удалены.
session.delete(instance)
db.session.query(User).filter(User.id == 101).delete()
# IntegrityError: (pymysql.err.IntegrityError) (1451, 'Cannot delete or update a parent row: a foreign key constraint fails (`test`.`user_info`, CONSTRAINT `user_info_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `dev_user` (`id`))')
instance = db.session.query(User).filter(User.id == 101).scalar()
db.session.delete(instance)
# 删除成功返回1
- Исправлять
Есть два способа изменить, изменить атрибут и затем отправить или изменить значение набора запросов.
# 修改对象属性
user = db.session.query(User).filter(User.id == 102).first()
user.username = 'Thomas'
db.session.add(user)
db.session.commit()
# 修改查询集Query的值
user_query = db.sessin.query(User.id < 100).update({'is_activate': 0})
db.session.commit()
- Запрос
filter_by
filter_byУсловие запроса=илиand, запрос с одним условием, условие должно иметь параметры ключевого слова, а соединение и возвращает объект запроса
# scalar只返回一个,如果没有或者有多个返回会抛出MultipleResultsFound异常
db.session.query(User).filter_by(id=101).scalar()
# 多个模型数据查询,默认查询第一个模型User的id为101的数据
db.session.query(User, UserInfo).filter_by(id=101).all()
filter
filterДопустимые типы параметров различаются, может быть несколько условий фильтрации, а возвращаемый объект запроса
В соответствии с идентификатором первичного ключа можно использовать напрямуюgetсделать запрос
db.session.query(User).get(100)
# 和Django一样,只获取一个结果,多个查询结果抛异常,没有则返回``None``
В отличие от Django ORM, sqlalchemy помещает некоторые условия в атрибуты поля модели, такие какorder_by,а такжеas
from sqlalchemy import desc, asc
query.order_by(desc(User.owned))
db.session.query(User.id).filter().order_by(asc(User.id)).offset(10).limit(10).all()
# SQL:
# SELECT dev_user.id AS dev_user_id
# FROM dev_user ORDER BY dev_user.id DESC
# LIMIT %(param_1)s, %(param_2)s
# {'param_1': 10, 'param_2': 10}
db.session.query(User.username.label('name')).filter()
# 将username起别名为name
# SQL:
# SELECT dev_user.username as name
# FROM dev_user
Во-первых, это некоторые методы для получения данных из запроса,all(),first(),one(),scalar(),get(),one_or_none()
-
все: вернуть все данные
-
first: взять первую строку во всех строках, если результата нет, будет None, и исключение не будет выдано.
-
one: возвращаемые строки должны содержать только одну строку. Если строк несколько или данных нет, будет выдано исключение.
-
one_or_none: возвращаемые строки должны быть одной строкой или не содержать данных, исключение будет выдано, если есть несколько строк данных.
-
скаляр: возвращаемые строки должны быть одной строкой или не иметь данных.Если есть несколько строк данных, будет выдано исключение, и скаляр возьмет только первый результирующий набор в результате.
=, запрос
# from sqlalchemy import true
db.session.query(User.id, User.username).filter(User.is_activate==db.true())
# 组合查询
db.session.query(User.username, UserInfo.company).filter(User.id==103, User.username=='Zachary Moreno').all()
# 组合查询也可以使用and_,翻译成的SQL相同
from sqlalchemy import and_, or_
db.session.query(User.username, UserInfo.company).filter(and_(User.id==103, User.username=='Zachary Moreno')).all()
# 翻译成的SQL:
# SELECT dev_user.username AS dev_user_username, user_info.company AS user_info_company
# FROM dev_user, user_info
# WHERE dev_user.id = %(id_1)s AND dev_user.username = %(username_1)s
# params: {'id_1': 103, 'username_1': 'Zachary Moreno'}
db.session.query(User).filter(User.id < 20).all()
db.session.query(User).filter(User.create_at > '2020-02-02 00:00:00')
нечеткий запрос
db.session.query(User.username).filter(User.email.startswith('123')).all()
db.session.query(User.username).filter(User.email.like('123%'))
# SELECT dev_user.username from dev_user where dev_user.email like '123%'
db.session.query(User.username).filter(User.email.endswith('123')).all()
join
присоединиться к операции таблицы соединения
db.session.query(Comment.comment.label('user_comment'), User.id.label('user_id'), User.username.label('username')).join(User, User.id==Comment.user_id).filter(User.id<100).order_by(User.id.desc()).all() # desc是字段的方法
# 对应SQL:
# SELECT comment.comment AS user_comment, dev_user.id AS user_id, dev_user.username AS username
# FROM comment INNER JOIN dev_user ON dev_user.id = comment.user_id
# WHERE dev_user.id = %(id_1)s
# params: {'id_1': 100}
Вы можете использовать псевдоним sqlalchemy для нескольких объединений.
from sqlalchemy.orm import aliased
Comment_alias = aliased(Comment, name='comment_alias')
db.session.query(User.id, User.username, User.email, User.phone, Comment.comment).join(Comment, Comment.user_id == User.id).join(Comment_alias, Comment_alias.user_id == User.id).all()
# SQL:
# SELECT dev_user.id AS dev_user_id, dev_user.username AS dev_user_username, dev_user.email AS dev_user_email, dev_user.phone AS dev_user_phone, comment.comment
# FROM dev_user INNER JOIN comment ON comment.user_id = dev_user.id INNER JOIN comment AS comment_alias ON comment_alias.user_id = dev_user.id
offset/limit
from sqlalchemy import true
db.session.query(User.username, User.phone, User.email).filter(User.is_active == true()).order(User.age.desc()).offset(10).limit(20)
# SQL:
# SELECT dev_user.username AS dev_user_username, dev_user.phone AS dev_user_phone, dev_user.email AS dev_user_email
# FROM dev_user
# WHERE dev_user.is_active = true ORDER BY dev_user.age DESC
# LIMIT %(param_1)s, %(param_2)s
# {'param_1': 10, 'param_2': 20}
in/not in/is null/is not null
# in
db.session.query(User).filter(User.id.in_([100, 103, 105]))
# not in
db.session.query(User).filter(~User.id.in_([100, 103, 105]))
# is null
from sqlalchemy import null
db.session.query(User).filter(User.email == null())
# is not null
db.session.query(User).filter(User.email != null())
select_from
Укажите, из какой таблицы данных брать данные, ведь получаемые поля можно указать прямо в запросе.select_fromа такжеjoinТаблицы в не могут быть одинаковыми
db.session.query(User).select_from(Comment).join(User, Comment.user_id == User.id).all()
# 只获取User中的数据,即query中的model
# SELECT dev_user.id AS dev_user_id, dev_user.username AS dev_user_username, dev_user.email AS dev_user_email, dev_user.is_active AS dev_user_is_active, dev_user.phone AS dev_user_phone, dev_user.age AS dev_user_age
# FROM comment INNER JOIN dev_user ON dev_user.id = comment.user_id