数据模型

3 模型

一、模型

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,自动生成sql语句,将程序中的对象自动保存到关系数据库中。优点:

  • 隐藏了数据库访问的细节,简化了sql的使用,提高了开发效率
  • 解耦业务逻辑层(view)和数据处理层(model),简化了开发流程,提高了系统的可移植性
  • 提高了安全性

缺点:

  • 执行效率低
  • 对复杂sql无能为力
  • 增加了学习成本

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

组成部分:

  • Engine,框架的引擎

  • Connection Pooling ,数据库连接池

  • Dialect,选择连接数据库的DB API种类

  • Schema/Types,架构和类型

  • SQL Exprression Language,SQL表达式语言

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

1.1 安装

pip install pymysql
pip install flask-sqlalchemy

1.2 配置链接数据的url地址

  • mysql
#'数据库类型+数据库驱动名称://用户名:口令@机器地址:端口号/数据库名'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/python1809flask'
  • sqlite
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'+os.path.join(os.getcwd(),'flask.sqlite')
  • 开启自动提交(否则为事物的提交)
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

二、设计模型

(1) 字段类型

类型 python中类型 说明
Integer int 整形
SmallInteger int 小整形
BigInteger int 长整形
Float float 浮点形
String string 字符串
Text string 长文本
Boolean bool 整形
Date datetime.date 日期
Time datetime.time 时间
DateTime datetime.datetime 日期和时间

(2) 约束条件

约束条件 说明
primary_key 主键
unique 唯一索引
index 常规索引
nullable 是否可以为null 默认True
default 默认值

注意:

  1. flask默认为空
  2. 设置外键不同 flask需要设置反向引用 多对多手动创建中间表
  3. flask默认开启事物 需要提交或者回滚(可以设置自动提交)
  4. 使用flask-sqlalchemy时要求每个模型都有一个主键,默认名字为id
  5. 模型类名与数据表中的名字
    • 默认:将大驼峰格式的模型类名,转换为小写加下划线格式,如:UserModel => user_model
    • 指定:__tablename__,使用此类属性指定表名

三、创建模型

(1) 创建用户模型

实例

from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy #导入ORM
from datetime import datetime
app = Flask(__name__)
#链接数据库地址
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/python1809flask'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app) #实例化模型
manager = Manager(app)#创建用户模型
class User(db.Model):__tablename__ = 'user' #起表名 默认是类名id = db.Column(db.Integer,primary_key=True) #主键username = db.Column(db.String(20),index=True)password_hash = db.Column(db.String(128))age = db.Column(db.Integer,default=20)sex = db.Column(db.Boolean,default=True)role = db.Column(db.Boolean,default=False) #角色(前台普通用户 还是后台管理员)register = db.Column(db.DateTime,default=datetime.utcnow) #注册时间confirm = db.Column(db.Boolean,default=False)

(2) 创建表

#创建表
@app.route('/create_all/')
def create_all():db.create_all()return 'create_all'

(3) 删除表

#删除表
@app.route('/drop_all/')
def drop_all():db.drop_all()return 'drop_all'

四、实现增删改查

(1) add 添加一条数据

#添加一条数据
@app.route('/add_one/')
def add_one():try:u = User(username='张三',password_hash='xkjbaskdadhajkdhadksjh')db.session.add(u) #添加一条数据db.session.commit() #提交except:db.session.rollback() #回滚return 'add_one'

(2) add_all 添加多条数据

#添加多条数据
@app.route('/add_many/')
def add_many():u1 = User()u1.username = '李四'u2 = User()u2.username = '王无'db.session.add_all([u1,u2])db.session.commit()return 'add_many'

(3) 修改数据

#修改
@app.route('/update/')
def update():u = User.query.get(1)u.username = '兆六'db.session.add(u)db.session.commit()return '修改

(4) delete 删除数据

#删除
@app.route('/delete/')
def delete():u = User.query.get(1)db.session.delete(u)db.session.commit()return '删除'

(5) 自定义一个base类 实现增删改

class DbBase:#添加一条数据def save(self):try:db.session.add(self)  # 添加一条数据db.session.commit()  # 提交except:db.session.rollback()#添加多条数据@staticmethoddef save_all(*args):try:db.session.add_all(args)db.session.commit()except:db.session.rollback()# 删除一条数据def delete(self):try:db.session.delete(self)  # 添加一条数据db.session.commit()  # 提交except:db.session.rollback()#创建用户模型
class User(db.Model,DbBase):...

使用

#添加一条数据
@test.route('/add_one/')
def add_one():u = User(username='李四', password_hash='xkjbaskdadhajkdhadksjh')u.save()return 'add_one'#添加多条数据
@test.route('/add_many/')
def add_many():u1 = User()u1.username = '兆六'u2 = User()u2.username = '王无'User.save_all(u1,u2)return 'add_many'#修改
@test.route('/update/')
def update():u = User.query.get(1)u.username = '张三三'u.save()return '修改'#删除
@test.route('/delete/')
def delete():u = User.query.get(1)u.delete()return '删除'

五、数据库查询

1.常见查询

方法 说明
get 根据主键进行查询,查到返回对象,没查到返回None
get_or_404 功能同上,查不到时,直接报404错
all 查询所有数据,返回一个***列表***(元素全是对象)
first 返回第一条数据,没有时返回None
first_or_404 功能同上,查不到时报404错
limit 限制结果集数量,返回查询对象,可以继续进行链式查询操作
offset 设置偏移量,返回查询对象,可以继续进行链式查询操作
order_by 结果集排序,可以指定多个字段,asc升序(默认),desc降序
count 统计总数

(1) all() 返回一个列表 包含所有对象

用法:类名.query.all()db.session.query(模型名).all()
u = User.query.all()[0:2]
u = User.query.all()
u = db.session.query(User.username,User.password).all()  #指定字段
u = db.session.query(User).all()

**注意:**不能进行链式调用 返回的不是查询集 而是列表(你只能再次使用列表的属性个方法了)

(2)first() 取出第一条数据

 data = User.query.first()print(data,type(data))

**注意:**取出的值为第一条数据是模型对象, 结果不可迭代

(3) get() 只能获取主键对应的数据 查询不到返回None

User.query.get(id值)

(4)字段列表

user = User.query.with_entities(User.username,User.sex).all()
user = db.session.query(User.username,User.sex).all()

(5)别名

 # 表别名ub = **aliased**(UserHobby,**name='ub'**)data = db.session.query(ub).all()#字段别名data = User.query.with_entities(User.username.**label('name')**, User.sex).all()

(6)distinct

    from sqlalchemy import distinctdata = db.session.query(User.sex).distinct().all()data = db.session.query(distinct(User.sex)).all()data = User.query.with_entities(distinct(User.sex)).all()

2.排序

  • 默认升序/asc()

  • -降序/desc()

data = User.query.order_by(User.age).limit(1) #取出年龄最小
data = User.query.order_by(-User.age).limit(1) #取出年龄最大
data = User.query.filter().order_by(-User.username,User.sex).all()
data = User.query.filter().order_by(User.username.desc(),User.sex.asc()).all()

3.聚合函数

  • 说明:max、min、sum、avg、count
  • 示例:
from sqlalchemy import func# 求最大值
max_age = db.session.query(func.max(User.age)).scalar()
num = db.session.query(User).count()

4.分组

  • group_by()

  • having()

  • 导入:from sqlalchemy import func

    func方法主要用来做统计,映射到sql语句中具体的统计方法,如:

    • func.count(..)
    • func.sum(..)
  num = func.count('*').label('c')data = db.session.query(User.sex,num).group_by(User.sex).having(num>3).all()data = db.session.query(User.sex,func.max(User.id).label('d')).group_by(User.sex).all()data = User.query.with_entities(User.sex,num).group_by(User.sex).having(num>3).all()

5. 限制

  • offset(num) 偏移num条数据

  • limit(num) 取num条数据

u = User.query.offset(4)
u = User.query.limit(4)
u = User.query.offset(1).limit(5)

6.条件查询

可以是哟filter和filter_by,ilter_by只用于单字段查询

  • 关系运算
  >,  __gt__>=,   __ge__<, __lt__<=, __le__==,   __eq__!=, __ne__如:users = User.query.filter(User.age > 20).all()# 与上面等价users = User.query.filter(User.age.__gt__(20)).all()
  • contains() 包含
  u = User.query.filter(User.username.contains('5'))u = User.query.filter(User.username.contains('5'),User.age>70)
  • like 模糊查询

    u = User.query.filter(User.username.like('%5'))
    u = User.query.filter(User.username.like('5%'))
    u = User.query.filter(User.username.like('%5%'))
    
- startswith endswith以...开头 以...结尾

u = User.query.filter(User.username.startswith(‘李’))
u = User.query.filter(User.username.endswith(‘六’))


- in 和 not in

u = User.query.filter(User.age.in_([10,20,30,67,68]))
u = User.query.filter(~User.age.in_([10,20,30,67,68]))
u = User.query.filter(User.age.notin_([10,20,30,67,68]))
u = User.query.filter(~User.age.notin_([10,20,30,67,68]))


- 判空 is null

u = User.query.filter(User.username==None)
u = User.query.filter(User.username.is_(None))
u = User.query.filter(User.username!=None)
u = User.query.filter(User.username.isnot(None))


- filter_by() 只支持单条件查询```python
User.query.filter_by()
User.query.filter_by(id=1)
User.query.filter_by(sex=True)
  • 逻辑运算
  from sqlalchemy import and_, or_,not_# 默认就是逻辑与# users = User.query.filter(User.id > 2, User.age > 20).all()# 与上式等价# users = User.query.filter(and_(User.id > 2, User.age > 20)).all()# 逻辑或users = User.query.filter(or_(User.id > 2, User.age > 20)).all()# 逻辑非u = User.query.filter(not_(User.sex==True,))

六、文件迁移

安装

  1. pip install flask-script
  2. pip install flask-migrate

实例

from flask import Flask
from flask_sqlalchemy import SQLalchemy
from flask_script import Manager
from flask_migrate import Migrate,MigrateCommandapp = Flask(__name__)
db = SQLalchemy(app) #实例化ORM模型
migrate = Migrate(app,db) #实例化迁移对象
manager = Manger(app)
manager.add_command('db',MigrateCommand) #添加迁移命令 别名为db

迁移步骤(命令)

(1) 迁移初始化(创建迁移目录)

python manage.py db init

(2) 创建迁移文件

python manage.py db migrate

(3) 执行迁移

python manage.py db upgrade

一 模型关系

  • 一对多(使用最多)

    • 一:学生(Student)

      • 添加反向引用
    • 多:文章(Article)
      • 添加外键关联
  • 一对一
    • 一:学生(Student)

      • 添加反向引用(在一对多的基础上多添加属性:uselist=False)
    • 一:详情(Profile)
      • 添加外键关联
  • 多对多
    • 多:学生(Student)

      • 需要添加反向引用
      • 需要使用secondary指定中间关联表
      • 设置反向查询数据的加载时机,需要使用:db.backref
    • 多:课程(Course)
    • 中间关联表:此表不需要用户维护
      • 表名
      • 关联外键

http://www.pythondoc.com/flask-sqlalchemy/models.html

# 学生模型
class Student(db.Model):id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(20), unique=True)''' 添加反向引用(无需迁移)参数1:关联的模型名backref:反向引用的字段名lazy:关联数据加载时机'select'/True:首次使用时自动查询,是默认选项'joined'/False:关联查询时使用'subquery':子查询时使用'dynamic':不加载数据,提供了关联数据的查询(不能用在一的一侧)'''articles = db.relationship('Article', backref='stu', lazy='dynamic')# 添加一对一的反向引用,需要设置:uselist=Falseprofile = db.relationship('Profile', backref='stu', uselist=False)# 添加多对多的反向引用,需要使用secondary指定中间关联表courses = db.relationship('Course', secondary='xuankebiao',backref=db.backref('students', lazy='dynamic'), lazy='dynamic')# 课程模型
class Course(db.Model):id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(20), unique=True)# 学生选课表(中间关联模型)
sc = db.Table('sc',db.Column('student_id', db.Integer, db.ForeignKey('student.id')),db.Column('course_id', db.Integer, db.ForeignKey('course.id'))
)
# 详情模型
class Profile(db.Model):id = db.Column(db.Integer, primary_key=True)realname = db.Column(db.String(20), unique=True)# 添加关联外键sid = db.Column(db.Integer, db.ForeignKey('student.id'))# 文章模型
class Article(db.Model):id = db.Column(db.Integer, primary_key=True)title = db.Column(db.String(32), unique=True)content = db.Column(db.Text)# 添加外键关联,需要指定关联的'表名.字段'sid = db.Column(db.Integer, db.ForeignKey('student.id'))

二 模型总结

  • 关联查询

    data = db.session.query(xuankebiao).join(Student).all()
    
  • 组合查询

    data1 = Student.query.filter(Student.id == 1)
    data2 = Student.query.filter(Student.id == 2)
    data = data1.union(data2).all()
    
  • 原生sql查询

    db.session.execute("select * from student").fetchall()
    db.session.execute(‘insert into users(name) values(:value)‘,params={"value":‘wupeiqi‘})db.session.commit()
    

视图函数中各种关系查询

@day.route('/')
def index():# for i in range(10):#     stu = Student()#     stu.name = "tom" + str(randint(1,1000))#     stu.sex = randint(0,1)#     stu.sclass = "190" + str(randint(1,5))#     db.session.add(stu)#     db.session.commit()# 查询# data = Student.query.with_entities(Student.name,Student.sex).all()# data = Student.query.with_entities(distinct(Student.sex)).all()## data = Student.query.order_by(-Student.sex,Student.sclass.desc()).all()# num = aliased(func.count('*'),name='num')# 字段别名# num = func.count('*').label('num')# data = Student.query.with_entities(Student.sclass,Student.sex,num).\#     group_by(Student.sclass,Student.sex).having(num > 1).all()# data = Student.query.offset(4).limit(5).all()# data = Student.query.filter(Student.sclass=='1903',Student.sex == 0).all()# data = Student.query.filter(Student.name.like('%8%')).all()# data = Student.query.filter(Student.sex.is_(None)).all()# data = Student.query.filter(Student.sex.isnot(None)).all()# data = Student.query.filter(Student.sex.isnot(None)).all()# print(data)# return render_template('index.html',students=data)return "ddd"# 一对多查询
@day.route('/one2mul')
def oneTOMany():# 正向查询 学生->文章# 返回的是学生对象 Student# stu = Student.query.get(2)# print(stu.id)# data = stu.articls.filter(Article.id<4).all()# print(data,type(data))# 反向查询 文章->学生article = Article.query.get(4)# # 从文章获取所属学生的对象# stud是Student的对象data = article.stud# print(data)# 关联查询# data = db.session.query(Student,Article).filter(Student.id == Article.sid,Student.id==2).all()# print(data)return "one to many"@day.route('/one2one')
def oneToOne():#正向查询# stu = Student.query.get(1)# print(stu.archives ,type(stu.archives))# 档案->学生arc = Archive.query.get(1)print(arc.stu,type(arc.stu))# print(Archive.query.get(1).stu)return "one to one"@day.route('/m2m')
def manyToMany():# student->course# stu = Student.query.get(1)# data = stu.courses.all()# print(data,type(data))# course->studentc = Course.query.get(2)data = c.students.all()# print(data)return "many"# 关联查询
@day.route('/relative')
def relative_query():# 内连接# data = db.session.query(Student.name,Article.title,Article.content).filter(Student.id == Article.sid).all()# 返回的是Student的信息#select distinct student.* from student join article on student.id = sid;# data = db.session.query(Student).join(Article).all()#select distinct article.* from student join article on student.id = sid;# data = db.session.query(Article).join(Student).all()# data = Student.query.join(Article).filter(Article.id==2).all()# 外链接# data = Student.query.outerjoin(Article).all()# data = db.session.query(Student,Article).outerjoin().filter(Student.id == Article.sid).all()# data = db.session.query(Article.id,Article.title).outerjoin(Student).all()# print(data)# data1 = Student.query.filter(Student.id == 1)# data2 = Student.query.filter(Student.id == 2)# print(type(data1))# data = data1.union(data2).all()# print(data)# 原生sqldata = db.session.execute("select * from student").fetchall()print(data)return "relation"

Flak模型和应用(一对一,一对多,多对多)相关推荐

  1. 7. MyBatis多表查询 - 一对一 - 一对多 - 多对多

    7. MyBatis多表查询 - 一对一 - 一对多 - 多对多 前言 在前面的篇章,我们已经熟悉了单表查询,下面我们来看看如何进行 多表查询. 数据准备 create database if not ...

  2. mybatis的一对一 一对多 多对多

    mybatis的一对一 一对多 多对多 1.表 2.建表语句 order_t表 CREATE TABLE `order_t` ( `id` int(11) NOT NULL, `user_id` in ...

  3. SQLAlchemy_定义(一对一/一对多/多对多)关系

    SQLAlchemy_定义(一对一/一对多/多对多)关系 目录 Basic Relationship Patterns One To Many One To One Many To Many Basi ...

  4. Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作

    Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作,单表查询,多表查询 一丶表与表之间的关系 背景: ​ ​ ​  ​ ​ 由于如果只使用一张表存储所有的数据,就会操作数 ...

  5. day 69-70 一对一 一对多 多对一联表查询

    day 69 orm操作之表关系,多对多,多对一多对一/一对多,多对多{类中的定义方法}day691. 昨日内容回顾1. 单表增删改查2. 单表查询API返回QuerySet对象的:1. .all() ...

  6. JPA 一对一 一对多 多对一 多对多配置

    1 JPA概述 1.1 JPA是什么 JPA (Java Persistence API) Java持久化API.是一套Sun公司 Java官方制定的ORM 方案,是规范,是标准 ,sun公司自己并没 ...

  7. Django一对一 ,一对多,多对多

    Django 数据库一对多,多对多 目录 app01/models.py app01/views.py app01/urls.py Python_Django/urls.py 目录 app01/mod ...

  8. JAVA日记之mybatis-3一对一,一对多,多对多xml与注解配置 ----喝最烈的酒.

    1.Mybatis多表查询 1.1 一对一查询 1.1.1 一对一查询的模型 用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户 一对一查询的需求:查询一个订单,与此同时查询出该订单 ...

  9. 初学者易上手的SSH-hibernate04 一对一 一对多 多对多

    这章我们就来学习下hibernate的关系关联,即一对一(one-to-one),一对多(one-to-many),多对多(many-to-many).这章也将是hibernate的最后一章了,用于初 ...

  10. 一对一 一对多 多对多

    一对一 例如,学校里一个班级只有一个正班长,而一个班长只在一个班中任职,则班级与班长之间具有一对一联系. 一对多 例如,一个班级中有若干名学生,而每个学生只在一个班级中学习,则班级与学生之间具有一对多 ...

最新文章

  1. CS9:转载:怎样配置Win Radius 当使用Cisco交换机时
  2. LiveGBS无插件播放页面的集成----单独的播放器样式
  3. 提高网速软件_【干货】C盘空间不够用,这款软件或许会帮到你!一键清除C盘多余文件...
  4. Netty 源码走读
  5. php7 ast,PHP7 的抽象语法树(AST)带来的变化
  6. 关于__str__和__repr__的用法和区别
  7. vscode 新版eslint自动修复_VSCODE 配置eslint规则和自动修复
  8. 读取文本节点_Python文本处理教程(2)
  9. C# Winform重启软件
  10. bzoj 3709 [PA2014]Bohater
  11. [BZOJ3503][CQOI2014]和谐矩阵
  12. 【数论】B043_机器人大冒险(取次数)
  13. Python爬虫:最牛逼的 selenium爬取方式!
  14. 高并发高负载网站系统架构
  15. Mac M1安装ta-lib
  16. 天还是那个天,从前却不再是那个从前
  17. NRF51822蓝牙服务(9)——动态修改设备的名称
  18. 【flutter】按钮,添加圆角
  19. Python制作手游《和平精英》游戏资料查询助手
  20. promethuesa监控 告警(二)

热门文章

  1. mybatis代码自动生成工具之maven插件mybatis-generator-maven-plugin(mybatis逆向工程)
  2. 职称计算机ppt教程,职称计算机:Word文档转为PPT的两种方法
  3. (DES)Single-Shot Object Detection with Enriched Semantics
  4. win32 010 使用masm32
  5. 小马哥----高仿苹果6s 主板型号S106s 更换内核 刷机拆机主板图与开机识别图
  6. 微信小程序选择地址填写详细地址定位地点
  7. MySQL 慢查询日志 使用方法浅析 日志定位与优化技巧
  8. rsync运行时出现skipping non-regular file
  9. Windows系统合并磁盘分区
  10. 联想小新310-14isk笔记本内存条的等等事