Flask应用篇

您所在的位置:网站首页 建立引擎 Flask应用篇

Flask应用篇

2023-07-06 00:29| 来源: 网络整理| 查看: 265

目录

一、简介

二、使用

(一)采用sqlalchemy方式

1. 基本:通过SQLAlchemy连接数据库并取数据

2. ORM介绍(SQLAlchemy强大的地方)

(二)、采用Flask-SQLAlchemy方式

 

一、简介

       SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。

       SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:

MySQL-Python mysql+mysqldb://:@[:]/ pymysql mysql+pymysql://:@/[?] MySQL-Connector mysql+mysqlconnector://:@[:]/ cx_Oracle oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...] 更多:http://docs.sqlalchemy.org/en/latest/dialects/index.html

       操作数据库有两种方式,一是使用flask_sqlalchemy,另一个是直接使用原始的sqlalchemy。        其中flask-sqlalchemy是对sqlalchemy进行了一些封装,提供了一些常用工具,使用更简洁。

二、使用 (一)采用sqlalchemy方式 1. 基本:通过SQLAlchemy连接数据库并取数据 # 0x01 通过SQLAlchemy连接数据库 from sqlalchemy import create_engine # 为数据库配置变量 HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'select_test' USERNAME = 'root' PASSWORD = 'toor' DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE) # 1 连接池的方式 # 创建数据库引擎, 创建数据库连接池,连接池中最大为5个。 # engine = create_engine(DB_URI, max_overflow = 0, pool_size = 5) # # 用with语句,只有连接成功才返回con,同时使用完毕后,自动结束连接 # # 连接数据库,执行sql语句,输出取得数据的第一条。 # with engine.contextual_connect() as con: # fs = con.execute('select * from teacher') # print(fs.fetchone()) # 2 普通的方式 engine = create_engine(DB_URI) conn = engine.connect() # result = conn.execute('select 1') result = conn.execute('select * from teacher') print(result.fetchone())

执行原生SQL

# 0x02 执行原生sql from sqlalchemy import create_engine from zl_config import DB_URI # 为数据库配置变量,写入配置文件 zl_config.py中 # HOSTNAME = '127.0.0.1' # PORT = '3306' # DATABASE = 'select_test' # USERNAME = 'root' # PASSWORD = 'toor' # DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE) # 创建数据库引擎, 创建数据库连接池,连接池中最大为5个。加入新参数echo=True(将操作数据库过程详细信息返回) engine = create_engine(DB_URI, max_overflow = 0, pool_size = 5, echo=True) # 0x02 # 使用with语句连接数据库 with engine.contextual_connect() as con: # 查看users表是否存在,如果存在就删除 !这个句子有问题 con.execute('drop table if exits users') # 创建users表,有字段id和name con.execute('create table users(id int primary key auto_increment, name varchar(25))') # 插入两条数据 con.execute('insert into users(name) values ("xiaoming")') con.execute('insert into users(name) values ("xiaozhao")') # # 执行查询操作 rs = con.execute('select * from users') print(rs.fetchall())

问题:drop table if exits users

原生sql的缺陷:

sql语句重复利用率不高,越复杂的sql语句条件越多,会出现很多相近的sql语句。很多sql语句是在业务逻辑中出来的,如果有数据库要更改,就要去修改这些逻辑,这会容易漏掉对某些sql语句的修改。写sql时,容易忽略web安全问题,安全隐患! 2. ORM介绍(SQLAlchemy强大的地方)

    ORM(Object Relationship Mapping),对象模型与数据库表的映射。 通过ORM,我们可以通过类的方式操作数据库,而不再写原生的SQL语句。通过把表映射成类,把行做实例,把字段作为属性,ORM在执行对象操作时,最终还是会把对应的操作转换为数据库原生语句。

     优点:

易用性:减少sql语句使用概率,代码直观清晰设计灵活:轻松写出复杂的查询可移植性:SQLAlchemy封装了底层的数据实现,支持多个关系数据库引擎,如MySQL、PostgreSQL、SQLite,可以很轻松的切换数据库。

     原理思路:类  ->  数据库中的一张表                        类中的属性(变量)   ->     数据库中那张表的字段                        类中的method            ->      数据库中的操作                        类创建的一个对象      ->     一条数据                        用类的method 执行数据库操作

原理图:

ORM模型:

# 0x03 ORM模型 from sqlalchemy import create_engine, Column, Integer, String from zl_config import DB_URI # declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承) from sqlalchemy.ext.declarative import declarative_base # 0.创建引擎,根据引擎创建基类 # 创建引擎(连接池的方式等后续教程) engine = create_engine(DB_URI) # 知识点:对象继承于类,类继承于原类 # 用declarative_base函数根据engine创建一个ORM基类。 # 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现) Base = declarative_base(engine) # 样表: # create table person(id int primary key autoincrement, name varchar(50), age int) # 1.创建一个ORM模型,这个模型必须继承来自SQLAlchemy给我们提供的基类。 class Person(Base): # 该ORM模型映射到数据库中的表的名字 __tablename__ = 'person' # 不写的话,该创建额表就是类名 # 2.在这个ORM模型中创建一些属性,来跟表中的字段一一映射。这些属性必须是SQLAlchemy提供好的数据类型。 id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(50)) age = Column(Integer) # 3.将创建好的ORM模型映射到数据库中。 Base.metadata.create_all() # 注意:一旦使用Base.metadata.create_all()将模型映射到数据库中后,即使改变了模型的字段,也不会重新映射了。

ORM数据类型:

    Integer:映射到数据库中式INT类型。    Float:浮点类型,映射到数据库中是float类型,占据32位。会存在精度丢失的问题,如果要求精度请用DECIMAL。    Double:双精度浮点类型,映射到数据库是double类型,占据64位。    Boolean:布尔类型,映射到数据库中是tinyint类型。    DECIMAL:定点类型,解决float和double丢失精度问题(存的原理好像是小数点在哪,就存在哪),存储时需要两个参数,第一个参数用来标记这个字段总能存储多少个数字,第二个参数表示小数点后有多少位。    enum:枚举类型,映射到数据库中是enum类型。    Date:传递datetime.date()进去。存储时间,只能存储年月日,映射到数据库中为date类型,在python中可以使用datetime.date这个模块来指定。    DateTime:传递datetime.datetime()进去。存储时间,可以存储年月日时分秒,映射到数据库中也是datetime类型,在python中可以使用datetime.datetime来指定。    Time:传递datetime.time()进去。存储时间,可以存储时分秒,映射到数据库中是time类型,在python中可以使用datetime.time来指定。    String:可变字符类型,映射到数据库中是varchar类型。使用时需要指定长度,区别于Text类型。    Text:存储长字符串,一般可以存储6w多个字符,映射到数据库为text类型,如果超出范围可以使用longtext类型。    LONGTEXT:长文本类型 (需要导入)因为这个数据类型只在mysql中有,所以导入地方为:from sqlalchemy.dialects.mysql import LONGTEXT。 # 0x04 ORM模型中映射到数据库中的数据类型 from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text from zl_config import DB_URI # declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker # 使用长文本类型 from sqlalchemy.dialects.mysql import LONGTEXT # 采用py3中的enum模块 import enum # 时间模块 from datetime import date from datetime import datetime from datetime import time # 0.创建引擎,根据引擎创建基类 # 创建引擎(连接池的方式等后续教程) engine = create_engine(DB_URI) # 知识点:对象继承于类,类继承于原类 # 用declarative_base函数根据engine创建一个ORM基类。 # 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现) Base = declarative_base(engine) # sessionmaker 功能待查 session = sessionmaker(engine)() # 定义自己的枚举 class TagEnum(enum.Enum): python = 'python' flask = 'flask' django = 'django' # 创建ORM模型 class Article(Base): __tablename__ = 'article' id = Column(Integer, primary_key=True, autoincrement=True) # price = Column(Float) is_delete = Column(Boolean) # Boolean类型对应数据库的tinyint类型 # DECIMAL(10,4):存储时需要两个参数,第一个参数用来标记这个字段总能存储多少个数字,第二个参数表示小数点后有多少位。 # ,位数超了就会报错。eg:100000.0001 price = Column(DECIMAL(10, 4)) # 后续存数据时,tag字段只能在‘python’、‘flask’、‘django’中取。 # tag = Column(Enum('python', 'flask', 'django')) # 采用自己定义的枚举类型,好处是防止写字符串时,由于错误导致sql语句错误(比如,写成"python1",但其实没有python1,只有python) tag = Column(Enum(TagEnum)) create_date = Column(Date) create_time = Column(DateTime) create_tiytime = Column(Time) content = Column(Text) content2 = Column(LONGTEXT) # 由于模型映射一次后,后续更改无效,故我们使用 flask-migrate, # 由于还没学到flask-migrate, # 所以使用Base.metadata.drop_all()把ORM模型删除 Base.metadata.drop_all() # 映射到数据库 Base.metadata.create_all() # 插入数据 # article = Article(is_delete=True, price=9.9999, tag='python') article = Article(is_delete=True, price=9.9999, tag=TagEnum.flask, create_date=date(2019, 9, 17), create_time=datetime(2019, 9, 17, 15, 2 ,10), create_tiytime=time(hour=15, minute=5, second=30), content="abd", content2="longabd") session.add(article) session.commit( )

 Column常用参数

default:默认值nullable:是否可空primary_key:是否为主键unique:是否唯一autoincrement:是否自增onupdate:更新时执行的函数name:该属性在数据库中的映射名,不指定的话使用这个属性名作为映射到数据库中的字段名

防止问题:请将这些指定参数放在数据类型后面。(至于问题,自己去查)

# 0x05 Column常用参数 from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text from zl_config import DB_URI # declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime # 0.创建引擎,根据引擎创建基类 # 创建引擎(连接池的方式等后续教程) engine = create_engine(DB_URI) # 知识点:对象继承于类,类继承于原类 # 用declarative_base函数根据engine创建一个ORM基类。 # 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现) Base = declarative_base(engine) # sessionmaker 功能待查 session = sessionmaker(engine)() class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) # default指定具体的值 read_count = Column(Integer, default=11) # create_time = Column(DateTime) # default指定函数 # 设置默认时间,注意是方法,不是方法的返回值 # create_time = Column(DateTime, default=datetime.now) # nullable默认为True title = Column(String(50), nullable=False, name='mytitle') # unique # tele = Column(String(50), unique=True) # !onupdate初次是为空的,只有更新了才会有值,故此处又加入了default update_time = Column(DateTime, onupdate=datetime.now, default=datetime.now) # 请搭配模型中字段值更改使用。(1. 2. 3.) # 3.name参数测试 # 1.基本的测试 Base.metadata.drop_all() Base.metadata.create_all() article = Article() # article.create_time = datetime.now() article.title = 'test' session.add(article) session.commit() # # 2.测试onupdate参数 # article = session.query(Article).first() # print(article.update_time) # # 更改title,以便查看是否更新了时间 # article.title = '563' # session.commit() # # 去数据库中看,是否更新时间了。 # article2 = session.query(Article).first() # print(article2.update_time)

query参数

模型对象,指定查找这个模型中所有对象。模型中的属性,指定只查找某个模型的其中几个属性。聚合函数: func.countfunc.avgfunc.maxfunc.minfunc.sum # 6.filter result = session.query(Article).filter(Article.id == 1).first() print(result) # 查看sql语句怎么写的,filter后不加其他。 result = session.query(Article).filter(Article.id == 1) print(result) result = session.query(Article).filter(Article.id != 1).all() print(result) # like、ilike(不区分大小写) result = session.query(Article).filter(Article.title.like('title%')).all() print(result) result = session.query(Article).filter(Article.title.in_(['title_ 1', 'title2'])).all() print(result) # not in :notin_() 或者用取反 ~

外键

       在mysql中,外键可以让便之间的关系更加紧密。而SQLAlchemy同样也支持外键。通过ForeignKey类来实现,并且可以指定表的外键约束。 

       外键约束又以下几项:

RESTRICT:父表数据被删除,会阻止删除。(默认)NO ACTION:在MySQL中,同RESTRICT。CASCADE:级联删除。SET NULL:父表数据被删除,子表数据会设置为NULL。 # 0x08 外键 from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey from zl_config import DB_URI # declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker # 0.创建引擎,根据引擎创建基类 # 创建引擎(连接池的方式等后续教程) engine = create_engine(DB_URI) # 知识点:对象继承于类,类继承于原类 # 用declarative_base函数根据engine创建一个ORM基类。 # 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现) Base = declarative_base(engine) # sessionmaker 功能待查 session = sessionmaker(engine)() # user表 class User(Base): __tablename__ = "user" id = Column(Integer, primary_key=True, autoincrement=True) username = Column(String(50), nullable=False) # article表 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) content = Column(Text, nullable=False) # 外键uid,类型一定要和关联的键值保持类型一致。 # 注意:ForeignKey()中写的是‘数据库’中的要关联的表和字段(!不是orm中!) # ondelete:用于传递外键约束(那四类里的) uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT")) # __repr__与__str__很相似,不过打印一个列表__repr__也可以返回 def __repr__(self): return "article.title:%s"%self.title # 1.模型映射 Base.metadata.drop_all() Base.metadata.create_all() # 2.添加数据 user = User(username="zhaoming") session.add(user) session.commit() article = Article(title="abc", content="123", uid=1) session.add(article) session.commit() # 3.删除表测试 # sql:delete from user where id = 1;

ORM层外键和一对多关系

# 0x08 外键、ORM层外键和一对多关系 from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey from zl_config import DB_URI # declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承) from sqlalchemy.ext.declarative import declarative_base # relationship用于外键查找 from sqlalchemy.orm import sessionmaker, relationship # 0.创建引擎,根据引擎创建基类 # 创建引擎(连接池的方式等后续教程) engine = create_engine(DB_URI) # 知识点:对象继承于类,类继承于原类 # 用declarative_base函数根据engine创建一个ORM基类。 # 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现) Base = declarative_base(engine) # sessionmaker 功能待查 session = sessionmaker(engine)() # user表 class User(Base): __tablename__ = "user" id = Column(Integer, primary_key=True, autoincrement=True) username = Column(String(50), nullable=False) # # 从一个作者找到其的所有文章 4.2 # articles = relationship("Article") def __repr__(self): return ""%(self.id, self.username) # article表 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) content = Column(Text, nullable=False) # 外键uid,类型一定要和关联的键值保持类型一致。 # 注意:ForeignKey()中写的是‘数据库’中的要关联的表和字段(!不是orm中!) # ondelete:用于传递外键约束(那四类里的) uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT")) # # 用于外键查找 4.1 # author = relationship("User") # 4.3 backref反向引用 author = relationship("User", backref="articles") # __repr__与__str__很相似,不过打印一个列表__repr__也可以返回 def __repr__(self): return ""%(self.id, self.title, self.content, self.uid) # # 1.模型映射 # Base.metadata.drop_all() # Base.metadata.create_all() # # 2.添加数据 # user = User(username="zhaoming") # session.add(user) # session.commit() # article = Article(title="abc", content="123", uid=1) # session.add(article) # session.commit() # # 3.删除表测试 # # sql:delete from user where id = 1; # 4.一对多关系 # # 4.1传统做法 # article = session.query(Article).first() # uid = article.uid # print(article) # # user = session.query(User).filter_by(id=uid).first() # user = session.query(User).get(uid) # print(uid) # print(user) # # 4.2 ORM relationship方式 # # article = session.query(Article).first() # # print(article.author.username) # user = session.query(User).first() # print(user) # print(user.articles[0].title) # 4.3 不用两边都写relationship user = session.query(User).first() print(user) print(user.articles[0].title)

通过"backref"指定反向访问的属性名。

注:articles有多个,故返回时时列表,取数据时要注意。

一对一关系

# 0x09 一对一关系 from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey from zl_config import DB_URI # declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承) from sqlalchemy.ext.declarative import declarative_base # relationship用于外键查找 from sqlalchemy.orm import sessionmaker, relationship, backref # 0.创建引擎,根据引擎创建基类 # 创建引擎(连接池的方式等后续教程) engine = create_engine(DB_URI) # 知识点:对象继承于类,类继承于原类 # 用declarative_base函数根据engine创建一个ORM基类。 # 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现) Base = declarative_base(engine) # sessionmaker 功能待查 session = sessionmaker(engine)() # user表 class User(Base): __tablename__ = "user" id = Column(Integer, primary_key=True, autoincrement=True) username = Column(String(50), nullable=False) # # 3.1 设置一对一关系 # extend = relationship("UserExtend", uselist=False) def __repr__(self): return ""%(self.id, self.username) # 3.ORM层面 # 将user不常用的字段放在另一张表中 # 注:user和userextend是一对一的关系 class UserExtend(Base): __tablename__ = "userextend" id = Column(Integer, primary_key=True, autoincrement=True) school = Column(String(50)) uid = Column(Integer, ForeignKey("user.id")) # # 3.1因为在User表中已经设置了,extend,所以不用反向写了 # user = relationship("User") # 3.2 采用backref函数 user = relationship("User", backref=backref("extend", uselist=False)) # article表 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) content = Column(Text, nullable=False) # 外键uid,类型一定要和关联的键值保持类型一致。 # 注意:ForeignKey()中写的是‘数据库’中的要关联的表和字段(!不是orm中!) # ondelete:用于传递外键约束(那四类里的) uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT")) # # 用于外键查找 4.1 # author = relationship("User") # 4.3 backref反向引用 author = relationship("User", backref="articles") # __repr__与__str__很相似,不过打印一个列表__repr__也可以返回 def __repr__(self): return ""%(self.id, self.title, self.content, self.uid) # 1.模型映射 Base.metadata.drop_all() Base.metadata.create_all() # # 2.基本 # # # 2.1创建数据:user添加两篇article # # user = User(username = "xiaoli") # # article1 = Article(title="abc", content="abcda") # # article2 = Article(title="czxc", content="zxcvbg") # # # user.articles是list类型 # # user.articles.append(article1) # # user.articles.append(article2) # # session.add(user) # # # 与user相关联的article1和article2都会自动添加 # # session.commit() # # 2.2反向添加:article添加user # user = User(username="xixi") # article = Article(title="sadf", content="werwe") # article.author = user # session.add(article) # session.commit() # # 3.ORM层面 # # 3.1 # user = User(username='zhil') # extend = UserExtend(school='zhil school') # user.extend = extend # session.add(user) # session.commit() # 3.2 采用backref函数 user = User(username='zhil') extend = UserExtend(school='zhil school') user.extend = extend session.add(user) session.commit()

多对多

思路:用中间表

先把两个需要做多对多的模型定义出来使用Table定义一个中间表,中间表一般就是包含两个模型的外键字段,并让它们两个作为一个复合主键在需要做多对多的模型之一上,定义一个relationship属性,来绑定三者之间的关系,在使用relationship时,需要传入一个secondary=中间表 # 0x010 多对多关系 (采用中间表) from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey, Table from zl_config import DB_URI # declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承) from sqlalchemy.ext.declarative import declarative_base # relationship用于外键查找 from sqlalchemy.orm import sessionmaker, relationship, backref # 0.创建引擎,根据引擎创建基类 # 创建引擎(连接池的方式等后续教程) engine = create_engine(DB_URI) # 知识点:对象继承于类,类继承于原类 # 用declarative_base函数根据engine创建一个ORM基类。 # 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现) Base = declarative_base(engine) # sessionmaker 功能待查 session = sessionmaker(engine)() article_tag = Table( "article_tag", Base.metadata, Column("article_id", Integer, ForeignKey("article.id"), primary_key=True), Column("tag_id", Integer, ForeignKey("tag.id"), primary_key=True) ) # article表 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) tags = relationship("Tag", backref="articles", secondary=article_tag) # tag表 class Tag(Base): __tablename__ = "tag" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(50), nullable=False) # 1.模型映射 Base.metadata.drop_all() Base.metadata.create_all() article1 = Article(title="1") article2 = Article(title="2") tag1 = Tag(name="tag1") tag2 = Tag(name="tag2") article1.tags.append(tag1) article1.tags.append(tag2) article2.tags.append(tag1) article2.tags.append(tag2) session.add(article2) session.add(article1) session.commit() # # 方便的获取其tag # article = session.query(Article).first() # article.tags

ORM层面删除数据注意事项

       ORM层面删除数据,会无视mysql级别的外键约束,直接会将对应的数据删除,然后将从表中的那个外键设置为NULL,如果想要避免这种行为,应该将从表中的外键的“nullable=False"

# 0x010 多对多关系 (采用中间表) from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DECIMAL, Enum, Date, DateTime, Time, Text, func, ForeignKey, Table from zl_config import DB_URI # declarative_base是一个函数,用于返回一个类(用于后续ORM模型中继承) from sqlalchemy.ext.declarative import declarative_base # relationship用于外键查找 from sqlalchemy.orm import sessionmaker, relationship, backref # 0.创建引擎,根据引擎创建基类 # 创建引擎(连接池的方式等后续教程) engine = create_engine(DB_URI) # 知识点:对象继承于类,类继承于原类 # 用declarative_base函数根据engine创建一个ORM基类。 # 用这个函数返回后续ORM需要继承的类(详细理解请看内部源代码实现) Base = declarative_base(engine) # sessionmaker 功能待查 session = sessionmaker(engine)() # 创建模型 # user表 class User(Base): __tablename__ = "user" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(50), nullable=False) class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) # uid = Column(Integer, ForeignKey("user.id")) # 解决 uid = Column(Integer, ForeignKey("user.id"), nullable=False) author = relationship("User", backref="articles") # Base.metadata.drop_all() # Base.metadata.create_all() # user = User(name="lili") # article = Article(title="asf") # article.author = user # session.add(user) # session.commit() # ORM层面会无视数据库层面的外键约束,这就是一个陷阱! # 解决:将外键那里设置为不能为空。 user = session.query(User).first() session.delete(user) session.commit()

relationship方法中的casecade参数详解

在SQLAlchemy中,只要将一个数据添加到session中,和它关联的数据都可以一起存入数据库。实现原理:通过relationship的一个关键参数casecade可以设置这些属性:

save-update:默认选项。在添加一条数据时,会把其他和他关联的数据都添加到数据库中。delete:表示当删除某一模型中的数据时,也删掉该模型中(关联的模型如果没有设置casecade="delete",那删除关联的模型执行的行为是默认行为,注意!单向通道的感觉)使用relationship和它关联的数据。delete-orphan:表示当对一个ORM对象解除了父表中的关联对象时,自己便会被删除掉。当如果父表中的数据被删除,自己也会被删除。这个选项只能用在一对多上,不能用在多对多与多对一上。并且需要在子模型中的relationship中,增加一个single_parent=True的参数。merge:默认选项,当在使用session.merge,合并一个对象的时候,会将使用了relationship相关联的对象也进行merge操作。expunge:移除操作时,会将相关联的对象也进行移除。这个操作只是从session中移除,并不会真正的从数据库中删除。all:是对save-updade,merge,refresh-expire,delete几种的缩写。 // 多个参数形式 casecade="save-update, delete"

排序(略)

limit、offset与切片操作(略)

复杂查询:group_by、having、join、subquery (略)

 

数据库查询懒加载技术

       在一对多,或者多对多时,如果想要获取多的这一部分的数据的时候,往往能通过一个属性就可以全部获取了。比如有一个作者,想要获取这个作者所有的文章,那么可以通过user.articles就可以获取所有的,但有时我们不想获取所有的数据,比如只想获取这个作者今天发表的文章,那么这时候我们可以给relationship传递一个lazy="dynamic",以后通过user.articles获取到的就不是一个列表,而是一个AppendQuery对象(这种对象既可以了添加新数据,也可以和“Query”对象一样,可以再进行一层过滤)。这样就可以对这个对象再进行一层过滤和排序等操作。

       lazy可用的选项:

"select":这个是默认选项。例子:程序中user.articles返回的是一个列表。"dynamic":返回一个“appendQuery”对象。 (二)、采用Flask-SQLAlchemy方式 1.基本

安装:

pip install flask_sqlalchemy

数据库连接:

和sqlalchemy一样,定义好数据库连接字符串DB_URI。将定义好的数据库连接字符串DB_URI,通过"SQLALCHEMY_DATABASE_URI"这个键放到"app.config"中。使用"flask_sqlalchemy.SQLAlchemy"这个类定义一个对象,并将"app"传入进去。 db = SQLAlchemy(app)

 

创建ORM模型 

用法和sqlalchemy一样,定义模型,现在不再是需要使用“delarative_base”来创建一个基类,而是使用“db.Model”来作为基类。在模型类中,‘Column’、‘String’、‘Integer’、‘relationship’等,都不需要导入,直接使用‘db’下面相应的属性名就可以了。在定义模型时,可以不写‘__tablename__’,那么flask_sqlalchemy会默认使用当前模型的名字转换成小写来作为表的名字,且如果这个名字使用多个单词且是驼峰命名法,则会在多个单词之间使用下划线来进行连接。(最好还是用__tablename__)

ORM模型映射到数据库

db.drop_all()db.create_all()

使用session

直接使用db.session

查询数据

如果查找数据只是查找一个模型上的数据,那么可以通过“模型.query”的方式进行查找。方法和sqlalchemy中的query方法一样。

from flask import Flask # 注意导入的是SQLAlchemy,这是一个类,不是sqlalchemy,这是一个包! from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) # 配置不变: 不过可以放在配置文件中 HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'flaskdb' USERNAME = 'root' PASSWORD = 'toor' DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE) app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI # 下面这个设置,请自己查,老师遇到了,自己写的没有遇到,估计是版本问题。 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) # 创建ORM模型 class User(db.Model): __tablename__ = 'user' # 因为封装了,所以不用单独再导入Column、Integer等等 id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(50)) def __repr__(self): return ""%self.username class Article(db.Model): __tablename__ = "article" id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(50), nullable=False) # 定义外键 uid = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) author = db.relationship("User", backref="articles") # # 将模型映射到数据库中 # db.drop_all() # db.create_all() # # 添加数据 # user = User(username="cc") # article = Article(title="luluxiu") # article.author = user # db.session.add(article) # db.session.commit() # # 查找数据 # # User.query 和 db.session.query(User)效果一样 # users = User.query.all() # 返回list # print(users) # # 排序: users = User.query.order_by(User.id.desc()).all() # # 其他 .filter # # .filter_by # # .group_by # # .join # # 修改 # user = User.query.filter(User.username=="cc").first() # user.username = 'gg' # db.session.commit() # 删除 article = Article.query.first() db.session.delete(article) db.session.commit() @app.route("/") def index(): return 'hello world' if __name__ == "__main__": app.run() 2.1(原生)alembic数据库迁移工具基本使用 (克服Base.drop_all()和Base.create_all()的缺陷)

      alembic是sqlalchemy的作者开发的,用来做ORM模型与数据库的迁移和映射。使用方式和git类似,表现在两个方面:第一,alembic的所有命令都是以alembic开头;第二,alembic的迁移文件也是通过版本进行控制的。(其本质也是对Base.drop_all()和Base.create_all()的花式利用,故当出现错误时,可以从这里考虑。)

      安装:pip install alembic

      使用:

初始化alembic仓库:(注意,若是在虚拟环境安装的,那么就要在虚拟环境下初始化)在终端中,cd到你的项目目录,然后执行命令alembic init alembic,创建一个名叫alembic的仓库。(alembic init [仓库名字])默认用alembic创建模型类:创建一个models.py模块,然后在里面定义你的模型类。修改配置文件: 在alembic(alembic.ini)中设置数据库连接,sqlalchemy.url = driver://user:pass@localhost/dbname  (sqlalchemy.url = mysql+pymysql://root:toor@localhost/almebic_demo?charset=utf8)。为了使用模型类更新数据,需要在env.py文件中设置target_metadata,默认为target_metadata=None。使用sys模块把当前项目的路径导入到path中。 import os import sys sys.path.append(os.path.dirname(os.path.dirname(__file__))) # 进行关联配置 import zl_sqlalchemy13_alembic # 进行关联配置 target_metadata = zl_sqlalchemy13_alembic.Base.metadata

4.将ORM模型生成迁移脚本:使用alembic revision --autogenerate -m "message"   (message:自己写的名字要清晰的反应这次做的版本操作是什么,比如:第一次提交、增加了什么字段等等,方便对不同版本的数据库进行升级或降级还原等。)

alembic revision --autogenerate -m "first commit"

       5.将生成的脚本映射到数据库中,使用alembic upgrade head 将刚刚生成的迁移文件,真正映射到数据库中;同理,如果要降级,那么使用alembic downgrade head

      6.修改代码后:(修改代码后,更新数据库,重复4、5步)

# 添加age age = Column(Integer, default=0) # 采用 alembic revision --autogenerate -m "add age column" # alembic upgrade head # 更新数据库

参考代码:

from sqlalchemy import Column, String, Integer, create_engine, ForeignKey from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship # 0.安装alembic:pip install alembic (注意:如果是安装在虚拟环境) # 1.初始化alembic仓库:alembic init alembic (那么就要在虚拟环境中初始化) # 2.创建模型类 class User(Base):... # 3.修改配置文件(alembic.ini): # import os # import sys # sys.path.append(os.path.dirname(os.path.dirname(__file__))) # # 进行关联配置 # import zl_sqlalchemy13_alembic # # 进行关联配置 # target_metadata = zl_sqlalchemy13_alembic.Base.metadata HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'almebic_demo' USERNAME = 'root' PASSWORD = 'toor' DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE) engine = create_engine(DB_URI) Base = declarative_base(engine) class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True, autoincrement=True) username = Column(String(50), nullable=False) # 添加age age = Column(Integer, default=0) # 采用 alembic revision --autogenerate -m "add age column" # alembic upgrade head # 更新数据库 # # 局限太大,后期上线不使用 # Base.metadata.create_all() # ORM->迁移脚本->映射到数据库 # 4.将ORM模型生成迁移脚本:alembic revision --autogenerate -m "first commit" # 5.将生成的脚本映射到数据库中,使用alembic upgrade head 将刚刚生成的迁移文件,真正映射到数据库中;同理,如果要降级,那么使用alembic downgrade head # 6.修改:重复4、5步。(注意:alembic revision --autogenerate -m ["message"] 中的message要根据具体情况更改)

常用命令

init:创建一个alembic仓库。revision:创建一个新的版本文件。-autogenerate:自动将当前模型的修改,生成迁移脚本。-m:本次迁移做了哪些修改,用户可以指定这个参数,方便回帧。upgrade:将指定版本的迁移文件映射到数据库中,会执行版本文件中的upgrade函数。如果有多个迁移脚本没有被映射到数据库中,那么会执行多个迁移脚本。[head]:代表最新的迁移脚本的版本号。downgrade:会执行指定版本的迁移文件中的downgrade函数。heads:展示head指向的脚本文件版本号。history:列出所有迁移版本及其信息。(只记录了upgrade的)current:展示当前数据库中的版本号。

此外,在第一次执行upgrade时,会在数据库中创建一个名叫alembic_version表,这个表只会有一条数据,记录当前数据库映射的是哪个版本的迁移文件。

经典错误

错误描述原因解决方法FAILED: Target database is not up to date.主要是heads和current不相同。current落后于heads的版本。将current移动到head上,alembic upgrade headFAILED:Can't locate revision identified by '77524dsfsf'数据库中存在版本号不在迁移脚本文件中删除数据库的alembic_version表中的数据,重新执行alembic upgrade head执行“upgrade head”命令报某个表已存在错误执行这个命令时,会执行所有的迁移脚本,因为数据库中已经存在了这个表,然后迁移脚本中又包含了创建表的代码。(1)删除versions中所有迁移文件。(2)修改迁移脚本中创建表的代码。

 

2.2 flask-SQLAlchemy下的alembic的配置

步骤:(安装一样)

初始化:alembic init alembic写模型类修改(alembic.ini):sqlalchemy.url = mysql+pymysql://root:toor@localhost/flaskdb?charset=utf8

                  (env.py)

import sys, os sys.path.append(os.path.dirname(os.path.dirname(__file__))) import zl_flask_sqlalchemy2_alembic target_metadata = zl_flask_sqlalchemy2_alembic.db.Model.metadata

4.5.6 (和之前一致)

 

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3