Model

作者: 十二右 | 来源:发表于2018-08-31 00:35 被阅读0次

    1. Flask模型

    Flask默认并没有提供任何数据库操作的API(Application Programming Interface,应用程序编程接口,是一些预先定义的函数
    )

    我们可以选择任何适合自己项目的数据库来使用

    Flask中可以自己的选择数据,用原生语句实现功能,也可以选择ORM(SQLAlchemy,MongoEngine)

    ORM:
    将对对象的操作转换为原生SQL
    优点 :

    易用性,可以有效减少重复SQL
    性能损耗少
    设计灵活,可以轻松实现复杂查询
    移植性好

    2. SQLAlchemy安装配置

    SQLAlchemy是一个很强大的关系型数据库框架,支持多种数据库后台。
    SQLAlchemy提供了高层ORM,也提供了使用数据库原生SQL的低层功能。

    针对于Flask的支持,官网地址

    pip install flask-sqlalchemy
    安装驱动
    pip install pymysql

    2.1 定义模型

    创建models.py文件,其中定义模型
    使用SQLALchemy的对象去创建字段

        from flask_sqlalchemy import SQLAlchemy
    
        db = SQLAlchemy()
    
    ​   
        class Student(db.Model):
    
            s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
            s_name = db.Column(db.String(16), unique=True)
            s_age = db.Column(db.Integer, default=1)
        
            __tablename__ = "student"
    

    其中:
    Integer : 表示创建的s_id字段的类型为整形,
    String : 表示该字段为字符串
    primary_key : 表示是否为主键
    unique : 表示该字段唯一
    default : 表示默认值
    autoincrement : 表示是否自增

    2.2 创建数据表

    在视图函数中我们引入models.py中定义的db

    from App.models import db
    
    @blue.route("/createdb/")
    def create_db():
        db.create_all()
        return "创建成功"
    
    @blue.route('/dropdb/')
    def drop_db():
        db.drop_all()
        return '删除成功'
    

    其中:
    db.create_all()表示创建定义模型中对应到数据库中的表
    db.drop_all()表示删除数据库中的所有的表

    2.3. 初始化SQLALchemy

    绑定sqlalchemy和app
    有两种方式:

    第一种:
    
    from flask_sqlalchemy import SQLALchemy
    
    app = Flask(__name__)
    db = SQLAlchemy(app)
    
    第二种:
    
    from App.models import db
    
    app = Flask(__name__)
    db.init_app(app)
    

    2.4 配置数据库的访问地址

    官网配置参数
    数据库连接的格式:
    dialect+driver://username:password@host:port/database
    dialect - 数据库实现
    driver - 数据库的驱动
    例子:
    访问mysql数据库,驱动为pymysql,用户为root,密码为123456,数据库的地址为本地,端口为3306,数据库名称HelloFlask
    文件中如下配置:
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:123456@localhost:3306/HelloFlask"

    3. 数据的CRUD

    语法:
    类名.query.xxx
    all() --> list结果集
    filter(类名.属性名==xxx) -->BaseQuery结果集
    filter_by(属性名=xxx) -->BaseQuery结果集
    get(id) -->object
    数据操作:
    在事务中处理,数据插入
    db.session.add(object)
    db.session.add_all(list[object])
    db.session.delete(object)
    db.session.commit()
    修改和删除基于查询

    3.1 添加数据 - create

    a.添加单一数据add()

    方法一:

    @blue.route('/createstu/')
    def create_stu():
        s = Student()
        s.s_name = '小花%d' % random.randrange(100)
        s.s_age = '%d' % random.randrange(30)
    
        db.session.add(s)
        db.session.commit()
        return '添加成功'
    

    方法二: 将提交操作封装为对象方法

    class Student(db.Model):
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        s_name = db.Column(db.String(10), unique=True)
        s_age = db.Column(db.Integer, default=10)
        s_g = db.Column(db.Integer, db.ForeignKey('grade.id'), nullable=True)
    
        __tablname__ = 'student'
    
        def save(self):
            db.session.add(self)
            db.session.commit()
    

    s.save()

    b. 批量添加add_all()
    @blue.route('create_many_stu/', methods=['GET'])
    def create_many_stu():
        if request.method == 'GET':
            # 批量添加学生信息
            stu_list = []
            num = random.randrange(1, 1000)
            for i in range(5):
                stu = Student()
                stu.s_name = '海神%s' % num
                stu.s_age = 30
                stu_list.append(stu)
                num += 1
            # add_all()批量添加学生,参数为学生对象列表
            db.session.add_all(stu_list)
            db.session.commit()
            return redirect(url_for('app.stu_paginate'))
    

    3.2 读取数据 - read

    a. 获取所有all()

    将学生的全部信息获取到,并且返回给页面,在页面中使用for循环去解析即可

    @blue.route("/getstudents/")
    def get_students():
        students = Student.query.all()
        return render_template("StudentList.html", students=students)
    
    b.条件查询filter()

    写法1:
    students = Student.query.filter(Student.s_id==1)
    写法2:
    students = Student.query.filter_by(s_id=2)
    注意:filter中可以接多个过滤条件
    写法3:
    sql = 'select * from student where s_id=1'
    students = db.session.execute(sql)

    3.3 修改数据 - update

    写法1:

    students = Student.query.filter_by(s_id=3).first()
    students.s_name = '哈哈'
    db.session.commit()
    

    写法2:

    Student.query.filter_by(s_id=3).update({'s_name':'娃哈哈'})
    db.session.commit()
    
    c 使用运算符

    获取查询集 :
    filter(类名.属性名.运算符(‘xxx’))
    filter(类名.属性 数学运算符 值)
    运算符:
    contains: 包含
    startswith:以什么开始
    endswith:以什么结束
    in_:在范围内
    like:模糊
    __gt__: 大于
    __ge__:大于等于
    __lt__:小于
    __le__:小于等于
    筛选:
    ​offset()
    limit()
    order_by()
    get()
    first()
    paginate()
    逻辑运算:
    与 and_ :
    filter(and_(条件),条件…)
    或 or_ :
    filter(or_(条件),条件…)
    非 not_ :
    filter(not_(条件),条件…)

    例子1:

    1. 查询学生的id为3,4,5,6,16的的学生信息,使用in_逻辑运算
      Student.query.filter(Student.s_id.in_([3,4,5,6,16]))

    2. 查询学生的年龄小于18岁的学生的信息
      Student.query.filter(Student.s_age < 18)
      Student.query.filter(Student.s_age.__lt__(18))

    3. 查询学生的姓名以什么开始或者以什么结尾的学生的信息startswith和endswith
      students = Student.query.filter(Student.s_name.startswith('张'))
      students = Student.query.filter(Student.s_name.endswith('2'))

    4. 查询id=4的学生的信息
      Student.query.get(4)
      获取的结果是学生的对象

    5. 模糊搜索like
      %:代表一个或者多个
      _:代表一个
      Student.query.filter(Student.s_name.like('%张%'))

    6. 分页,查询第二页的数据4条 ​
      第一个参数是那一页,第二个参数是一页的条数,第三个参数是是否输出错误信息
      students = Student.query.paginate(2, 4, False).items

    7. 按照id降序排列
      from sqlalchemy import desc, asc
      stus = Student.query.order_by('-s_id')
      按照id升序排列
      stus = Student.query.order_by('s_id')
      stus = Student.query.order_by(asc('s_id'))
      stus = Student.query.order_by('s_id asc')
      按照id降序获取三个
      stus = Student.query.order_by('-s_id').limit(3)

    8. 跳过3个数据,查询5个信息
      stus = Student.query.order_by('-s_age').offset(3).limit(5)

    9. 查询多个条件
      from sqlalchemy import and_, or_, not_
      stus = Student.query.filter(Student.s_age==18, Student.s_name=='雅典娜')

      • and_ 并且条件
        stus = Student.query.filter(and_(Student.s_age==18, Student.s_name=='雅典娜'))

      • or_ 或者条件
        stus = Student.query.filter(or_(Student.s_age==18, Student.s_name=='火神'))

      • not_ 非
        stus = Student.query.filter(not_(Student.s_age==18), Student.s_name=='火神')

      • 查询姓名不包含'可爱‘,并且年龄不等于12的学生
        stus = Student.query.filter(not_(Student.s_name.contains('可爱')), not_(Student.s_age == 12))

    3.4 删除数据 - delete

    写法1:

    students = Student.query.filter_by(s_id=2).first()
    db.session.delete(students)
    db.session.commit()
    

    写法2:

    students = Student.query.filter_by(s_id=1)
    db.session.delete(students[0])
    db.session.commit()
    

    注意:在增删改中如果不commit的话,数据库中的数据并不会更新,只会修改本地缓存中的数据,所以一定需要db.session.commit()

    3.5 分页- paginate:

    属性列表
    image.png


    后端数据处理:

    # 方法一:手动实现分页,使用offset和limit
    page = int(request.args.get('page', 1))
    stus = Student.query.offset((page-1)*5).limit(5)
    
    # 方法二: 使用切片[:]
    s_page = (page - 1)*5
    e_page = page * 5
    stus = Student.query.all()[s_page: e_page]
    
    # 方法三:使用paginate
    # 查询第几页的数据  
    page = int(request.args.get('page', 1))
    
    # 每一页的条数多少,默认为10条
    per_page = int(request.args.get('per_page', 10))
    
    # 查询当前第几个的多少条数据
    paginate = Student.query.order_by('-s_id').paginate(page, per_page, error_out=False)
    
    stus = paginate.items
    

    前端数据展示:

    <h2>学生信息</h2>
    {% for stu in stus %}
        id:{{ stu.s_id }}
        姓名:{{ stu.s_name }}
        年龄:{{ stu.s_age }}
        <br>
    {% endfor %}
    <br>
    总页数: {{ paginate.pages }}
    <br>
    一共{{ paginate.total }}条数据
    <br>
    当前页数:{{ paginate.page }}
    <br>
    {% if paginate.has_prev %}
        <a href="/stupage/?page={{ paginate.prev_num }}">上一页</a>:{{ paginate.prev_num }}
    {% endif %}
    
    {% if paginate.has_next %}
        <a href="/stupage/?page={{ paginate.next_num }}">下一页</a>:{{ paginate.next_num }}
    {% endif %}
    <br>
    
    <br>
    页码:{% for i in  paginate.iter_pages() %}
            <a href="/stupage/?page={{ i }}">{{ i }}</a>
        {% endfor %}
    

    4. 关联关系

    4.1 一对多

    a. 建立模型

    学生模型:

    class Student(db.Model):
    
        s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        s_name = db.Column(db.String(20), unique=True)
        s_age = db.Column(db.Integer, default=18)
        s_g = db.Column(db.Integer, db.ForeignKey('grade.g_id'), nullable=True)
    
        __tablename__ = 'student'
    

    班级模型:

    class Grade(db.Model):
    
        g_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        g_name = db.Column(db.String(10), unique=True)
        g_desc = db.Column(db.String(100), nullable=True)
        g_time = db.Column(db.Date, default=datetime.now)
        students = db.relationship('Student', backref='stu', lazy=True)
    
        __tablename__ = 'grade'
    

    官网解释有如下几个lazy的参数:

    lazy 决定了 SQLAlchemy 什么时候从数据库中加载数据:,有如下四个值:

    select(True) : (which is the default) means that SQLAlchemy will load the data as necessary in one go using a standard select statement.
    joined(False): tells SQLAlchemy to load the relationship in the same query as the parent using a JOIN statement.
    subquery: works like ‘joined’ but instead SQLAlchemy will use a subquery.
    dynamic: is special and useful if you have many items. Instead of loading the items SQLAlchemy will return another query object which you can further refine before loading the items. This is usually what you want if you expect more than a handful of items for this relationship
    ====================================
    select :就是访问到属性的时候,就会全部加载该属性的数据。
    joined :则是在对关联的两个表进行join操作,从而获取到所有相关的对象。
    dynamic :则不一样,在访问属性的时候,并没有在内存中加载数据,而是返回一个query对象, 需要执行相应方法才可以获取对象,

    b. 数据操作
    1. 通过班级查询学生信息
        @grade.route('/selectstubygrade/<int:id>/')
    
        def select_stu_by_grade(id):
            grade = Grade.query.get(id)
            # 通过班级对象.定义的relationship变量去获取学生的信息
            stus = grade.students
        
            return render_template('grade_student.html',
                                   stus=stus,
                                   grade=grade
                                   )
    
    1. 通过学生信息查询班级信息
        @stu.route('/selectgradebystu/<int:id>/')
    
        def select_grade_by_stu(id):
    
            stu = Student.query.get(id)
            # 通过学生对象.定义的backref参数去获取班级的信息
            grade = stu.stu
        
            return render_template('student_grade.html',
                                   grade=grade,
                                   stu=stu)
    

    注意:
    表的外键由db.ForeignKey指定,传入的参数是表的字段。
    db.relations它声明的属性不作为表字段,第一个参数是关联类的名字,backref是一个反向身份的代理,相当于在Student类中添加了stu的属性。
    例如:
    有Grade实例dept和Student实例stu。
    dept.students.count()将会返回学院学生人数;
    stu.stu.first()将会返回学生的学院信息的Grade类实例。
    一般来讲db.relationship()会放在一这一边。

    4.2 多对多

    a. 定义模型:

    1. 引入SLALchemy
      from flask_sqlalchemy import SQLAlchemy
      db = SQLAlchemy()
    2. 创建中间表
    sc = db.Table('sc',
        db.Column('s_id', db.Integer, db.ForeignKey('student.s_id'), primary_key=True),
        db.Column('c_id', db.Integer, db.ForeignKey('courses.c_id'), primary_key=True)
    )
    
    1. 创建学生类Student
        class Student(db.Model):
    
            s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
            s_name = db.Column(db.String(20), unique=True)
            s_age = db.Column(db.Integer, default=18)
            s_g = db.Column(db.Integer, db.ForeignKey('grade.g_id'), nullable=True)
        
            __tablename__ = 'student'
        
            def __init__(self, name, age):
        
                self.s_name = name
                self.s_age = age
                self.s_g = None
    
    1. 课程类 Course
        class Course(db.Model):
            c_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
            c_name = db.Column(db.String(20), unique=True)
            students = db.relationship('Student',
                                       secondary=sc,
                                       backref='cou')
        
            __tablename__ = 'courses'
        
            def __init__(self, name):
        
                self.c_name = name
    

    sc表由db.Table声明,我们不需要关心这张表,因为这张表将会由SQLAlchemy接管,它唯一的作用是作为students表和courses表关联表,所以必须在db.relationship()中指出sencondary关联表参数**。
    lazy是指查询时的惰性求值的方式,这里有详细的参数说明,而db.backref是声明反向身份代理,其中的lazy参数是指明反向查询的惰性求值方式.

    b. 数据操作
    1. 添加学生和课程之间的关系
      通过页面中传递学生的id和课程的id,分别获取学生的对象和课程的对象,在使用关联关系append去添加学生对象,并且add以后再commit后,就可以在中间表sc中查看到新增的关联关系了。

       userid = request.form.get('userid')
       courseid = request.form.get('courseid')
      
       stu = Student.query.get(userid)
       cou = Course.query.get(courseid)
      
       cou.students.append(stu)
       db.session.add(cou)
       db.session.commit()
      
    2. 删除学生和课程之间的关系
      通过页面获取传递的学生的id和课程的id,分别获取学生对象和课程对象,在使用关联关系remove去删除学生对象,并commit将事务提交到数据库中

      stu = Student.query.get(s_id)
      cou = Course.query.get(c_id)

      cou.students.remove(stu)
      db.session.commit()

    3. 通过课程查询学生的信息
      以下定义在课程course的模型中,所以通过课程查询学生的信息,语法为 :课程的对象.students。如果知道学生的信息反过来找课程的信息,则使用backref的反向关联去查询,语语法为 : 学生的对象.cou(反向)

    students = db.relationship('Student',secondary=sc,backref='cou')
    
        cou = Course.query.get(2)
        stus = cou.students
    
    1. 通过学生去查询课程的信息
        stu = Student.query.get(id)
        cous = stu.cou
    

    5. 数据库迁移

    在django中继承了makemigrations,可以通过migrate操作去更新数据库,修改我们定义的models,然后在将模型映射到数据库中。

    在flask中也有migrate操作,它能跟踪模型的变化,并将变化映射到数据库中

    2.1 安装migrate

    pip install flask-migrate
    

    2.2 配置使用migrate

    2.2.1 初始化,使用app和db进行migrate对象的初始化
    from flask_migrate import Migrate
    
    #绑定app和数据库
    Migrate(app=app, db=db)
    
    2.2.2 安装了flask-script的话,可以在Manager()对象上添加迁移指令
    from flask_migrate import Migrate, MigrateCommand
    
    app = Flask(__name__)
    
    manage = Manager(app=app)
    
    manage.add_command('db', MigrateCommand)
    

    操作:

    python manage.py db init  初始化出migrations的文件,只调用一次
    
    python manage.py db migrate  生成迁移文件
    
    python manage.py db upgrade 执行迁移文件中的升级
    
    python manage.py db downgrade 执行迁移文件中的降级
    
    python manage.py db --help 帮助文档
    

    相关文章

      网友评论

          本文标题:Model

          本文链接:https://www.haomeiwen.com/subject/nnphwftx.html