原文:https://blog.csdn.net/u014793102/article/category/9285123
Flask从入门到做出一个博客的大型教程(三)
在开始之前,先来看下项目的整体结构。
1 flask 2 ├── app 3 │ ├── forms.py 4 │ ├── __init__.py 5 │ ├── routes.py 6 │ └── templates 7 │ ├── base.html 8 │ ├── index.html 9 │ └── login.html 10 ├── config.py 11 ├── myblog.py
4 数据库
对于一个完整的网站数据库是很重要的,因为你的数据得有位置读取呀,网上很多数据库都用的sqlite,但是,我想使用mysql,所以接下来咱们就以mysql为数据库来讲解喽。
1 (venv) duke@coding:~/flask_tutorial/flask$ pip install flask-sqlalchemy
这样就可以对数据库进行操作了,但是实际项目中会经常对数据库进行修改,但是一般不会手动的去数据库里进行改动,通常的做法是修改ORM对应的模型,然后再把模型映射到数据库中。在flask里有一个集成的工具是专门做这个事情的,安装它。
1 (venv) duke@coding:~/flask_tutorial/flask$ pip install flask-migrate
因为我使用的是MySQL数据库,而在python3中不再支持mysqldb,因此我们还需要安装pymysql.
1 (venv) duke@coding:~/flask_tutorial/flask$ pip install pymysql
需要的组件都安装好了,接下来在配置文件里配置数据库。
app/config.py : 使用配置文件config.py中的内容
1 import os 2 BASE_DIR = os.path.abspath(os.path.dirname(__file__)) 3 class Config(object): 4 #....... 5 #格式为mysql+pymysql://数据库用户名:密码@数据库地址:端口号/数据库的名字?数据库格式 6 SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:123456@localhost:3306/flaskblog?charset=utf8' 7 #如果你不打算使用mysql,使用这个连接sqlite也可以 8 #SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR,'app.db') 9 SQLALCHEMY_TRACK_MODIFICATIONS = False
配置文件设置好了以后,就要对初始化文件进行修改。
app/_ _ init_ _.py : 数据库的初始化设置
1 from flask import Flask 2 from config import Config 3 from flask_sqlalchemy import SQLAlchemy 4 from flask_migrate import Migrate 5 app = Flask(__name__) 6 app.config.from_object(Config) 7 #建立数据库关系 8 db = SQLAlchemy(app) 9 #绑定app和数据库,以便进行操作 10 migrate = Migrate(app,db) 11 12 from app import routes,models
接下来比较重要的就是设计模型了,model是模型的映射,只有设计好model才能进行一系列的操作。新建一个models.py文件。
1 (venv) duke@coding:~/flask_tutorial/flask$ touch app/models.py
app/models.py : 用户数据库模型
1 from app import db 2 3 class User(db.Model): 4 __tablename__ = 'user' 5 id = db.Column(db.Integer,primary_key=True) 6 username = db.Column(db.String(64),index=True,unique=True) 7 email = db.Column(db.String(120),index=True,unique=True) 8 password_hash = db.Column(db.String(128)) 9 10 def __repr__(self): 11 return '<用户名:{}>'.format(self.username)
对模型进行验证:
1 (venv) duke@coding:~/flask_tutorial/flask$ python 2 Python 3.6.4 (default, May 3 2018, 19:35:55) 3 [GCC 5.4.0 20160609] on linux 4 Type "help", "copyright", "credits" or "license" for more information. 5 >>> from app.models import User 6 >>> u = User(username='duke',email='duke@126.com') 7 >>> u 8 <用户名:duke>
对模型验证后,发现是正确的,进行数据库初始化。
1 (venv) duke@coding:~/flask_tutorial/flask$ flask db init 2 Creating directory /home/duke/flask_tutorial/flask/migrations ... done 3 Creating directory /home/duke/flask_tutorial/flask/migrations/versions ... done 4 Generating /home/duke/flask_tutorial/flask/migrations/script.py.mako ... done 5 Generating /home/duke/flask_tutorial/flask/migrations/alembic.ini ... done 6 Generating /home/duke/flask_tutorial/flask/migrations/README ... done 7 Generating /home/duke/flask_tutorial/flask/migrations/env.py ... done 8 Please edit configuration/connection/logging settings in 9 '/home/duke/flask_tutorial/flask/migrations/alembic.ini' before proceeding.
现在看一看项目结构
1 flask 2 ├── app 3 │ ├── forms.py 4 │ ├── __init__.py 5 │ ├── models.py 6 │ ├── routes.py 7 │ └── templates 8 │ ├── base.html 9 │ ├── index.html 10 │ └── login.html 11 ├── config.py 12 ├── migrations 13 │ ├── alembic.ini 14 │ ├── env.py 15 │ ├── README 16 │ ├── script.py.mako 17 │ └── versions 18 ├── myblog.py
接下来创建数据库的管理工具。
1 (venv) duke@coding:~/flask_tutorial/flask$ flask db migrate -m 'users_table' 2 INFO [alembic.runtime.migration] Context impl MySQLImpl. 3 INFO [alembic.runtime.migration] Will assume non-transactional DDL. 4 INFO [alembic.autogenerate.compare] Detected added table 'user' 5 INFO [alembic.autogenerate.compare] Detected added index 'ix_user_email' on '['email']' 6 INFO [alembic.autogenerate.compare] Detected added index 'ix_user_username' on '['username']' 7 Generating 8 /home/duke/flask_tutorial/flask/migrations/versions/06ea43ff4439_users_table.py 9 ... done
创建数据库的中的表。
1 (venv) duke@coding:~/flask_tutorial/flask$ flask db upgrade 2 INFO [alembic.runtime.migration] Context impl MySQLImpl. 3 INFO [alembic.runtime.migration] Will assume non-transactional DDL. 4 INFO [alembic.runtime.migration] Running upgrade -> 06ea43ff4439, users_table
执行到这里,表已经在数据库中建立完成。或许你会问,这和我以前的接触过的不一样啊,这么会麻烦这么多,因为你对数据库模型修改后,可以很方便的进行数据库表的迁移,这会大大的减少以后可能会发生的大量的工作量。
网站里不可能只有一张表的,多张表之间肯定会有联系,接下来对表之间的关系进行一个详细介绍。
app/models.py : 添加一张新表,并建立关系
1 from datetime import datetime 2 from app import db 3 4 class User(db.Model): 5 __tablename__ = 'user' 6 id = db.Column(db.Integer,primary_key=True) 7 username = db.Column(db.String(64),index=True,unique=True) 8 email = db.Column(db.String(120),index=True,unique=True) 9 password_hash = db.Column(db.String(128)) 10 # back是反向引用,User和Post是一对多的关系,backref是表示在Post中新建一个属性author,关联的是Post中的user_id外键关联的User对象。 11 #lazy属性常用的值的含义,select就是访问到属性的时候,就会全部加载该属性的数据;joined则是在对关联的两个表进行join操作,从而获取到所有相关的对象;dynamic则不一样,在访问属性的时候,并没有在内存中加载数据,而是返回一个query对象, 需要执行相应方法才可以获取对象,比如.all() 12 posts = db.relationship('Post',backref='author',lazy='dynamic') 13 14 def __repr__(self): 15 return '<用户名:{}>'.format(self.username) 16 17 class Post(db.Model): 18 __tablename__ = 'post' 19 id = db.Column(db.Integer,primary_key=True) 20 body = db.Column(db.String(140)) 21 timestamp = db.Column(db.DateTime,index=True,default=datetime.utcnow) 22 user_id = db .Column(db.Integer,db.ForeignKey('user.id')) 23 24 def __repr__(self): 25 return '<Post {}>'.format(self.body)
生成新的数据库关系:
1 (venv) duke@coding:~/flask_tutorial/flask$ flask db migrate -m 'posts_table' 2 INFO [alembic.runtime.migration] Context impl MySQLImpl. 3 INFO [alembic.runtime.migration] Will assume non-transactional DDL. 4 INFO [alembic.autogenerate.compare] Detected added table 'post' 5 INFO [alembic.autogenerate.compare] Detected added index 'ix_post_timestamp' on '['timestamp']' 6 Generating /home/miguel/microblog/migrations/versions/780739b227a7_posts_table.py ... done
提交到数据库中:
1 (venv) duke@coding:~/flask_tutorial/flask$ flask db upgrade 2 INFO [alembic.runtime.migration] Context impl MySQLImpl. 3 INFO [alembic.runtime.migration] Will assume non-transactional DDL. 4 INFO [alembic.runtime.migration] Running upgrade -> 780739b227a7, posts_table
接下来使用shell对进行操作:
1 (venv) duke@coding:~/flask_tutorial/flask$ python 2 Python 3.6.4 (default, May 3 2018, 19:35:55) 3 [GCC 5.4.0 20160609] on linux 4 Type "help", "copyright", "credits" or "license" for more information. 5 >>> from app import db 6 >>> from app.models import User,Post
在用户表中添加一条用户信息:
1 #创建一个User对象实例 2 >>> u = User(username='duke',email='duke@126.com') 3 #将实例添加 4 >>> db.session.add(u) 5 #提交 6 >>> db.session.commit()
查看数据库后发现,插进一条数据。
在用户表中添加另外一条用户信息:
1 >>> u = User(username='king',email='king@qq.com') 2 >>> db.session.add(u) 3 >>> db.session.commit()
通过查询数据库发现也插进了一条数据,但是怎么查询的呢?
查询数据库中信息:
1 >>> users = User.query.all() 2 >>> users 3 [<用户名:duke>, <用户名:king>] 4 >>> for u in users: 5 ... print(u.id,u.username) 6 ... 7 1 duke 8 2 king
这是一次性全部查询,那能不能查单条数据呢?答案是肯定的喽。
1 #根据id可以查询到数据 2 >>> u = User.query.get(2) 3 >>> u 4 <用户名:king>
普通的数据库表咱们会了,那么有外键关联的一对多中数据库表怎么插入数据呢?
1 #查找到一个User对象 2 >>> u = User.query.get(1) 3 #将该User对象与Post建立关系 4 >>> p = Post(body='我第一次提交数据!',author=u) 5 >>> db.session.add(p) 6 >>> db.session.commit() 7 #插入第二条数据,但是拥有者都是同一个人 8 >>> p = Post(body='我第二次提交数据了!',author=u) 9 >>> db.session.add(p) 10 >>> db.session.commit()
现在通过查询来让你更加的熟悉一对多的关系。
1 # 获得一个用户的所有提交 2 >>> u = User.query.get(1) 3 >>> u 4 <用户名:duke> 5 #u.posts.all()中的posts是model中的,User和Post关联的作用。 6 >>> posts = u.posts.all() 7 >>> posts 8 [<Post 我第一次提交数据!>, <Post 我第二次提交数据了!>] 9 10 #进行相同的操作,但是换成另外一名用户 11 >>> u = User.query.get(2) 12 >>> u 13 <用户名:king> 14 >>> u.posts.all() 15 [] 16 17 #对所有的posts进行查询 18 >>> posts = Post.query.all() 19 >>> for p in posts: 20 ... print(p.id,p.author.username,p.body) 21 ... 22 1 duke 我第一次提交数据! 23 2 duke 我第二次提交数据了! 24 25 #还可以按照一定的规则进行查询 26 >>> User.query.order_by(User.username.desc()).all() 27 [<用户名:king>, <用户名:duke>]
熟悉了查询,接下来就把刚才的测试数据都删除吧!
1 >>> users = User.query.all() 2 >>> for u in users: 3 ... db.session.delete(u) 4 ... 5 >>> posts = Post.query.all() 6 >>> for p in posts: 7 ... db.session.delete(p) 8 ... 9 >>> db.session.commit()