6.ORM与SQLAlchemy (2) - 模型关系与引用
承接上文,我们的Q&A demo
,除了用户表,还需要存储所有问题内容的表questions_info
和存储所有评论的表comments_info
,并且都和users_info
通过外键来关联。我们不排除后续需要更多表的可能性,把所有模型和视图函数写在一起看着也太混乱了!为此,我们新建一个models.py
,把三个模型都放在这里。
由于是新建的models.py文件,我们同样要先在开头生成一个名为db
的SQLAlchemy
对象:
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy()
前文中我们给SQLAlchemy
传入了Flask
对象app
作为参数,这里是不是也要从视图函数文件HarpQA.py
导入那个app并传进去呢?并不可以,因为HarpQA.py
也要使用到db
(如db.session
),这样就产生了循环引用,所以在这里不能传入app
,而是回到HarpQA.py
,使用db.init_app(app)
将app
和db
绑定,避免了循环引用。
users_info
表(Users
模型)代码如下:
class Users(db.Model): __tablename__ = 'users_info' id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(32), nullable=False) password = db.Column(db.String(100), nullable=False) register_time = db.Column(db.DateTime, nullable=False, default=datetime.now()) # 我们新增了一个avatar_path字段来存用户头像图片文件的路径 avatar_path = db.Column(db.String(256), nullable=False, default='images/doraemon.jpg')
questions_info
表(Questions
模型)代码如下:
class Questions(db.Model): __tablename__ = 'questions_info' id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(100), nullable=False) content = db.Column(db.TEXT, nullable=False) author_id = db.Column(db.Integer, db.ForeignKey('users_info.id')) create_time = db.Column(db.DateTime, nullable=False, default=datetime.now()) author = db.relationship('Users', backref=db.backref('questions', order_by=create_time.desc()))
这个表存储所有问题的标题、内容、创建时间、作者ID,作者ID通过外键与用户表的ID关联,方式也很简单,在db.Column
中用db.ForeignKey('users_info.id')
作为参数即可。
再看最后一条语句:
author = db.relationship('Users', backref=db.backref('questions', order_by=create_time.desc()))
db.relationship
会自动找到两个表的外键,建立Questions
和Users
的关系,此时对于任意一个Questions
对象question
,通过question.author
就可获得这个question
的作者对应的Users
对象,例如获取id
为1
的问题的作者姓名:
question = Questions.query.filter(Questions.id == 1).first() author_name = question.author.username
db.relationship
的第二个参数backref=db.backref('questions', order_by=create_time.desc())
则建立了一个反向引用,这样我们不仅可以使用question.author
,还可以使用author.questions
获得一个作者所有的问题,并通过order_by=create_time.desc()
按创建时间倒序排列(网页的内容按时间倒序排列),返回的是一个Questions
对象的列表,可以遍历它获取每个对象,如获取作者Harp
的所有问题的title
:
author = Users.query.filter(Users.username == 'Harp').first() for question in author.questions: print(question.title)
同理,comments_info
表(Comments
模型)代码如下:
class Comments(db.Model): __tablename__ = 'comments_info' id = db.Column(db.Integer, primary_key=True, autoincrement=True) content = db.Column(db.TEXT, nullable=False) question_id = db.Column(db.Integer, db.ForeignKey('questions_info.id')) author_id = db.Column(db.Integer, db.ForeignKey('users_info.id')) create_time = db.Column(db.DateTime, nullable=False, default=datetime.now()) author = db.relationship('Users', backref=db.backref('comments')) question = db.relationship('Questions', backref=db.backref('comments', order_by=create_time.desc()))
在HarpQA.py
中,我们要从models.py
导入db
及所有的模型
,注意因为上下文的关系,我们这里用with
语句把app
推入栈中:
from flask import Flask, render_template from models import db, Users, Questions, Comments import config app = Flask(__name__) app.config.from_object(config) db.init_app(app) with app.test_request_context(): db.drop_all() db.create_all() @app.route('/') def index(): return render_template('home.html') if __name__ == '__main__': app.run()
运行脚本,此时数据库已经把三张表都建立好了: