flask基础fifth,flask-script, 增删改查,flask-SQLAlchemy
一、多app应用
多个app实例(启用)
from werkzeug.wsgi import DispatcherMiddleware from werkzeug.serving import run_simple from flask import Flask, current_app app1 = Flask('app01') app2 = Flask('app02') @app1.route('/index') def index(): return "app01" @app2.route('/index2') def index2(): return "app2" # http://www.oldboyedu.com/index # http://www.oldboyedu.com/sec/index2 dm = DispatcherMiddleware(app1, { '/sec': app2, }) if __name__ == "__main__": run_simple('localhost', 5000, dm)
#请求来了,会执行dm()--->__call__
二、flask-script(制定命令)
1 模拟出类似django的启动方式:python manage.py runserver
2 pip install flask-script
3 把excel的数据导入数据库,定制个命令,去执行(openpyxl)
python manage.py insertdb -f xxx.excel1 -t aa (从excel1中读取数据插入到aa表里面,-f就是from , -t就是to)
4 使用
-方式一:python manage.py runserver
from flask import Flask from flask_script import Manager app = Flask(__name__) manager=Manager(app) if __name__ == '__main__': manager.run() #此时执行命令就是在终端输入python 脚本名字 runserver
` -方式二:自定制命令
from flask import Flask from flask_script import Manager app = Flask(__name__) manager=Manager(app) #自定义命令 @manager.command def custom(arg): print(arg) #此时执行方法就是:python manage.py custom 123(传入的参数) @manager.option('-n', '--name', dest='name') @manager.option('-u', '--url', dest='url') def cmd(name, url): ''' 自定义命令(-n也可以写成-name) 执行:Python manage.py cmd -n wmt -u http://www.baidu.com 执行:python manage.py cmd --name wmt --url http://www.baidu.com ''' print(name, url) if __name__ == '__main__': manager.run()`
5 创建超级用户
6 现在有很多条excel用户,批量导入到数据库中
-navicate直接支持
-脚本
-flask-script
三、sqlachemy
第三方orm框架(对象关系映射)
-go语言中 :gorm,xorm
-python中:django orm,sqlachemy,peewee ,django orm只能在django中用,不能单独用
1 使用: pip install sqlachemy
2 SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件
3 补充:django orm反向生成models
-python manage.py inspectdb > app/models.py
基本使用(原生sql)
import time import threading import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.engine.base import Engine # 第一步生成一个engine对象 engine = create_engine( "mysql+pymysql://root:123@127.0.0.1:3306/flask?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) # 第二步:创建连接(执行原生sql) conn = engine.raw_connection() # 第三步:获取游标对象 cursor = conn.cursor() # 第四步:具体操作 cursor.execute('select * from boy') res=cursor.fetchall() print(res) # 比pymysql优势在,有数据库连接池
orm的使用:
先创建表,以及建立一对多、多对多关系表
# 创建一个个类(需清楚继承谁,字段写法) import datetime from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base # 字段和字段属性 from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index # 制造了一个类,作为所有模型类的基类 Base = declarative_base() class User(Base): __tablename__ = 'users' # 数据库表名称(固定写法),如果不写,默认以类名小写作为表的名字 id = Column(Integer, primary_key=True) # id 主键 # mysql中主键自动建索引:聚簇索引 # 其他建建的索引叫:辅助索引 name = Column(String(32), index=True, nullable=False) # name列,索引列,不可为空 email = Column(String(32), unique=True) # 唯一 #datetime.datetime.now不能加括号,加了括号,以后永远是当前时间 ctime = Column(DateTime, default=datetime.datetime.now) # default默认值 extra = Column(Text, nullable=True) #类似于djagno的 Meta __table_args__ = ( UniqueConstraint('id', 'name', name='uix_id_name'), #id和name联合唯一,且取名为uix_id_name Index('ix_id_name', 'name', 'email'), #索引 )
#一对多关系表
class hobby(Base):
__tablename__='hobby'
id = Column(Integer,primary_key=True)
caption = Column(String(50),default='篮球')
class Person(Base):
__tablename__='hobby'
nid = Column(Integer,primary_key=True)
name =Column(String(32),index=True,nullable=True)
#hobby指的是tablename而不是类名,uselist=False
hobby_id = Column(Integer,ForeignKey("hobby.id")) #建立一对多关系,且需要建立在多的一方
#多对多
#多对多肯定有一张第三方表,如下面的boy2girl
class Boy2Girl(Base):
__tablename__='boy2girl'
id = Column(Integer,primary_key=True,autocrement=True)
girl_id =Column(Integer,ForeignKey("girl.id"))
boy_id =Column(Integer,ForeignKey("boy.id"))
class Boy(Base):
__tablename__='boy'
id = Column(Integer,primary_key=True)
name =Column(String(64),index=True,nullable=True)
class Girl(Base):
__tablename__='girl'
id = Column(Integer,primary_key=True)
name =Column(String(64),index=True,nullable=True)
# 创建表 def create_table(): # 创建engine对象 engine = create_engine( "mysql+pymysql://root:123@127.0.0.1:3306/aaa?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) # 通过engine对象创建表 Base.metadata.create_all(engine) # 删除表 def drop_table(): # 创建engine对象 engine = create_engine( "mysql+pymysql://root:123@127.0.0.1:3306/aaa?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) # 通过engine对象删除所有表 Base.metadata.drop_all(engine) if __name__ == '__main__': create_table() #drop_table() # 需要手动创建库 # sqlachemy不支持修改字段吗
通过orm给表插入一条数据
from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from models import User # pycharm报错,不会影响我们 from sqlalchemy.orm import scoped_session # 1 制作engine engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5) # 2 制造一个 session 类(会话) Session = sessionmaker(bind=engine) # 得到一个类 # 3 得到一个session对象 session=Session() # 4 创建一个对象 obj1 = User(name="wmt") # 5 把对象通过add放入 session.add(obj1) # 6 提交 session.commit()
线程安全(多线程情况下):
#基于scoped_session实现线程安全 from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from models import User from sqlalchemy.orm import scoped_session # 1 制作engine engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5) # 2 制造一个 session 类(会话) Session = sessionmaker(bind=engine) # 得到一个类 # 3 得到一个session对象(线程安全的session) #现在的session已经不是session对象了 #线程安全,还是用的local session = scoped_session(Session) # 4 创建一个对象 obj1 = User(name="2008") # 5 把对象通过add放入 session.add(obj1) # session.aaa() # 6 提交 session.commit() session.close()
# 类不继承Session类,但是有该类的所有方法(是因为通过反射,一个个放进去的)
orm的增删改
from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from models import User,Person,Hobby from sqlalchemy.orm import scoped_session from sqlalchemy.sql import text engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5) Session = sessionmaker(bind=engine) #线程安全 # session = scoped_session(Session) session=Session() #1 新增多个对象 obj=User(name='xxx') obj2=User(name='yyyy') obj3=User(name='zzz') #新增同样对象 session.add_all([obj,obj2,obj3]) #新增不同对象 session.add_all([Person(name='lqz'),Hobby()]) #2 简单删除(查到删除) res=session.query(User).filter_by(name='2008').delete() #filter_by传的是参数 res=session.query(User).filter(User.id>=2).delete() #filter传的是表达式 # 返回1,代表影响1行 print(res) # 3 修改 res=session.query(User).filter_by(id=1).update({User.name:'ccc'}) res=session.query(User).filter_by(id=1).update({'name':'ccc'}) session.query(User).filter(User.id > 0).update({User.name: User.name + "099"}, synchronize_session=False) # 如果要把它转成字符串相加,synchronize_session=False必须有 session.query(User).filter(User.id > 0).update({"age": User.age + 1}, synchronize_session="evaluate") # 如果要把它转成数字相加, synchronize_session="evaluate"必须有
#4.简单查
res=session.query(User).all()
res=session.query(User).first()
res=session.query(User).filter(User.id>=1).all()
高级操作:
from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from models import User,Person,Hobby from sqlalchemy.sql import text engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5) Session = sessionmaker(bind=engine) session=Session() # 1 查询名字为lqz的所有user对象 ret = session.query(User).filter_by(name='ccc099').all() # 2 表达式,and条件连接 ret = session.query(User).filter(User.id > 1, User.name == 'egon').all() # 3查找id在1和10之间,并且name=egon的对象 ret = session.query(User).filter(User.id.between(1, 10), User.name == 'egon').all() # 4in条件(class_,因为这是关键字,不能直接用) ret = session.query(User).filter(User.id.in_([1,3,4])).all() # 5取反 ~ ret = session.query(User).filter(~User.id.in_([1,3,4])).all() #6二次筛选(相当于sql子查询) ret = session.query(User).filter(User.id.in_(session.query(User.id).filter_by(name='egon'))).all() # 指定具体字段的子查询 ret = session.query(User.id,User.name).filter(User.id.in_(session.query(User.id).filter_by(name='egon'))).all() ################ from sqlalchemy import and_, or_ #or_包裹的都是or条件,and_包裹的都是and条件 #查询id>3并且name=egon的人 ret = session.query(User).filter(and_(User.id > 3, User.name == 'egon')).all() # 查询id大于2或者name=ccc099的数据 ret = session.query(User).filter(or_(User.id > 2, User.name == 'ccc099')).all() ret = session.query(User).filter( or_( User.id < 2, and_(User.name == 'egon', User.id > 3), User.extra != "" )).all() print(ret) # 通配符,以e开头,不以e开头 ret = session.query(User).filter(User.name.like('e%')).all() ret = session.query(User).filter(~User.name.like('e%')).all() # 限制,用于分页,区间 limit # 前闭后开区间,1能取到,3取不到 ret = session.query(User)[1:3] # 排序,根据name降序排列(从大到小) ret = session.query(User).order_by(User.name.desc()).all() ret = session.query(User).order_by(User.name.asc()).all() #第一个条件降序排序后,再按第二个条件升序排 ret = session.query(User).order_by(User.id.asc(),User.name.desc()).all() ret = session.query(User).order_by(User.name.desc(),User.id.asc()).all() # 分组 from sqlalchemy.sql import func ret = session.query(User).group_by(User.name).all() #分组之后取最大id,id之和,最小id # sql 分组之后,要查询的字段只能有分组字段和聚合函数 ret = session.query( func.max(User.id), func.sum(User.id), func.min(User.id), User.name).group_by(User.name).all() for obj in ret: print(obj[0],'----',obj[1],'-----',obj[2],'-----',obj[3]) print(ret) #haviing筛选,分组之后再筛选 ret = session.query( func.max(User.id), func.sum(User.id),
func.min(User.id)).group_by(User.name).having(func.min(User.id) >2).all()
多表操作:一对多
from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from models import User,Person,Hobby,Boy,Girl,Boy2Girl from sqlalchemy.sql import text engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5) Session = sessionmaker(bind=engine) session=Session() ### 1 一对多插入数据 obj=Hobby(caption='足球') session.add(obj) p=Person(name='张三',hobby_id=2) session.add(p) ### 2 方式二(默认情况传对象有问题),需要有relationship字段 #Person表中要加 hobby = relationship('Hobby', backref='pers') p=Person(name='李四',hobby=Hobby(caption='美女')) # 等同于 p=Person(name='李四2') p.hobby=Hobby(caption='美女2') session.add(p) ###3 方式三,通过反向操作 hb = Hobby(caption='王五') hb.pers = [Person(name='王六'), Person(name='王七')] session.add(hb) ###4 查询(查询:基于连表的查询,基于对象的跨表查询) #4.1 基于对象的跨表查询(子查询,两次查询) # 正查 p=session.query(Person).filter_by(name='张三').first() print(p) print(p.hobby.caption) # 反查 h=session.query(Hobby).filter_by(caption='王五').first() print(h.pers) #4.2 基于连表的跨表查(查一次) # 默认根据外键连表 # isouter=True 左外连,表示Person left join Hobby,没有右连接,只需要改变表名即可 # 不写 inner join person_list=session.query(Person,Hobby).join(Hobby,isouter=True).all() print(person_list) for row in person_list: print(row[0].name,row[1].caption) ret = session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id) print(ret) #join表,默认是inner join ret = session.query(Person).join(Hobby) ret = session.query(Hobby).join(Person,isouter=True) print(ret) # 指定连表字段(从来没用过) ret = session.query(Person).join(Hobby,Person.nid==Hobby.id, isouter=True) ret = session.query(Person).join(Hobby,Person.hobby_id==Hobby.id, isouter=True).all() print(ret) # 组合。UNION 操作符用于合并两个或多个 SELECT 语句的结果集 # union和union all的区别:union会去重,而union all不会去重 q1 = session.query(User.name).filter(User.id > 2) q2 = session.query(User.name).filter(User.id < 8) q1 = session.query(User.id,User.name).filter(User.id > 2) q2 = session.query(User.id,User.name).filter(User.id < 8) ret = q1.union_all(q2).all() ret1 = q1.union(q2).all() print(ret) print(ret1) q1 = session.query(User.name).filter(User.id > 2) q2 = session.query(Hobby.caption).filter(Hobby.nid < 2) ret = q1.union_all(q2).all()
多表操作:多对多
session.add_all([ Boy(hostname='男明星1'), Boy(hostname='男明星2'), Girl(name='女明星1'), Girl(name='女明星2'), ]) session.add_all([ Boy2Girl(girl_id=1, boy_id=1), Boy2Girl(girl_id=2, boy_id=1) ]) ##### 要有girls = relationship('Girl', secondary='boy2girl', backref='boys') girl = Girl(name='女明星3') girl.boys = [Boy(hostname='男明星3'),Boy(hostname='男明星4')] session.add(girl) boy=Boy(hostname='男明星5') boy.girls=[Girl(name='女明星4'),Girl(name='女明星5')] session.add(boy) session.commit() # 基于对象的跨表查 girl=session.query(Girl).filter_by(id=3).first() print(girl.boys) #基于连表的跨表查询 # 查询男明星4约过的所有妹子 ''' 原生sql select girl.name from girl,boy,Boy2Girl where boy.id=Boy2Girl.boy_id and girl.id=Boy2Girl.girl_id where boy.name='男明星4' ''' ret=session.query(Girl.name).filter(Boy.id==Boy2Girl.boy_id,Girl.id==Boy2Girl.girl_id,Boy.hostname=='男明星4').all() ''' 原生sql select girl.name from girl inner join Boy2Girl on girl.id=Boy2Girl.girl_id inner join boy on boy.id=Boy2Girl.boy_id where boy.hostname='男明星4' ''' ret=session.query(Girl.name).join(Boy2Girl).join(Boy).filter(Boy.hostname=='男明星4').all() ret=session.query(Girl.name).join(Boy2Girl).join(Boy).filter_by(hostname='男明星4').all() print(ret) ### 执行原生sql(用的最多的) #django中orm执行原生sql是另外的方法 cursor = session.execute('insert into users(name) values(:value)',params={"value":'xxx'}) print(cursor.lastrowid) session.commit() session.close()
四、将上面的集成到flask-SQLAlchemy
1 Flask-SQLAlchemy
2 flask-migrate
-python3 manage.py db init 初始化:只执行一次
-python3 manage.py db migrate 等同于 makemigartions
-python3 manage.py db upgrade 等同于migrate
3 Flask-SQLAlchemy如何使用
1 from flask_sqlalchemy import SQLAlchemy
2 db = SQLAlchemy()
3 db.init_app(app)
4 以后在视图函数中使用
-db.session 就是咱们讲的session
4 flask-migrate的使用(表创建,字段修改)
1 from flask_migrate import Migrate,MigrateCommand
2 Migrate(app,db)
3 manager.add_command('db', MigrateCommand)
5 直接使用
-python3 manage.py db init 初始化:只执行一次,创建migrations文件夹
-python3 manage.py db migrate 等同于 makemigartions
-python3 manage.py db upgrade 等同于migrate
6具体使用:
# 安装 pip install flask-sqlalchemy # 所有的导入都找下面的db from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy()
flask-migrate
安装: pip install flask-migrate 命令:manager.add_command('db1', MigrateCommand) # 当项目第一次执行迁移的时候,只需要初始化一次 1、python3 manage.py db1 init 2、python3 manage.py db1 migrate # 等同于django的makemigrations 3、python3 manage.py db1 upgrade # 等同于django的migrate
account.py
from flask import Blueprint from .. import db from .. import models account = Blueprint('account', __name__) @account.route('/login') def login(): # db.session.add(models.Users(username='wmt', email='123@qq.com')) # #db.session.query(models.Users).all() # db.session.commit() # 添加示例 """ db.session.add(models.Users(username='lqz', pwd='123', gender=1)) db.session.commit() obj = db.session.query(models.Users).filter(models.Users.id == 1).first() print(obj) PS: db.session和db.create_session """ # db.session.add(models.Users(username='ali1', email='ali1@xx.com')) # db.session.commit() # db.session.close() # db.session.add(models.Users(username='ali2', email='ali2@xx.com')) # db.session.commit() # db.session.close()
# db.session.add(models.Users(username='alx1',email='alx1@live.com')) # db.session.commit() # db.session.close() user_list = db.session.query(models.Users).all() for item in user_list: print(item.username,"is",item.email) return 'login'
__init__.py
from flask import Flask from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() # from .models import * from .views import account def create_app(): app = Flask(__name__) app.config.from_object('settings.BaseConfig') # 将db注册到app中 db.init_app(app) # 注册蓝图 app.register_blueprint(account.account) return app
models.py
from . import db class Users(db.Model): """ 用户表 """ __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) def __repr__(self): return '<User %r>' % self.username
manage.py
from .sansa import create_app from flask_script import Manager
# flask_migrate管理数据迁移的 from flask_migrate import Migrate,MigrateCommand from .sansa import db app = create_app() manager=Manager(app) # 将当前app,与db注册到Migrate Migrate(app,db) # 添加管理数据的命令 manager.add_command('db1', MigrateCommand) if __name__ == '__main__': manager.run()
settings.py
class BaseConfig(object): # SESSION_TYPE = 'redis' # session类型为redis # SESSION_KEY_PREFIX = 'session:' # 保存到session中的值的前缀 # SESSION_PERMANENT = True # 如果设置为False,则关闭浏览器session就失效。 # SESSION_USE_SIGNER = False # 是否对发送到浏览器上 session:cookie值进行加密 SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:@127.0.0.1:3306/day96?charset=utf8" SQLALCHEMY_POOL_SIZE = 5 SQLALCHEMY_POOL_TIMEOUT = 30 SQLALCHEMY_POOL_RECYCLE = -1 # 追踪对象的修改并且发送信号 SQLALCHEMY_TRACK_MODIFICATIONS = False class ProductionConfig(BaseConfig): return('开发配置') class DevelopmentConfig(BaseConfig): return('上线配置') class TestingConfig(BaseConfig): return('测试配置')