今天会实作资料库的结构。我们总共需要实作三个 table 的 scheme,分别是 users
、posts
、comments
。
等等会提到一些资料库的概念,我不会细讲,因为要真的细讲的话大概需要好几天的文章,此处我们先知道怎麽用就好。在这篇文章的最後我会补充一下一些用法。
我们需要把这些 model 都放在昨天提到的 app/database/models.py
里面。我们一个一个来看,先从 users
开始。先不要管最前面的引入,我们晚点再看。
from werkzeug.security import generate_password_hash, check_password_hash
class Users(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, unique=True, nullable=False)
password = db.Column(db.String, nullable=False)
email = db.Column(db.String, unique=True, nullable=False)
introduction = db.Column(db.String)
is_admin = db.Column(db.Boolean, nullable=False, default=False)
register_time = db.Column(
db.DateTime, default=datetime.datetime.now, nullable=False
)
posts = db.relationship("Posts")
comments = db.relationship("Comments")
def __init__(self, username, password, email, introduction=None, is_admin=False):
self.username = username
self.password = generate_password_hash(password)
self.email = email
self.introduction = introduction
self.is_admin = is_admin
def check_password(self, password):
return check_password_hash(self.password, password)
我们宣告了一个叫做 Users
的物件,而他是继承自 db.Model
,这个 db
就是昨天用到的那个。我们在这里面放了很多变数,除了 __tablename__
之外 (很明显他就代表这个 table 的名字),每一个变数都是一个资料库的 column,一样一个一个分开来看。
id
是这个 table 的主键 (primary key),如同他後面的参数 primary_key=True
所示。在他前面有一个 db.Integer
,他表示了这个 column 的资料库型别。有了前面这两个参数,autoincrement (自动把 id 流水号) 就会自动存在。username
是用来存使用者名称的,他使用的资料库型别是 db.String
,此外,他还加上 unique
和 nullable
两个参数,分别代表是否唯一及可否为空,那在此处当然是要唯一并不可为空,毕竟是使用者名称,不这样做也有点奇怪。password
是存密码用的,当然,我们存的不是明文,而是 hash 过的结果,这个部分会在等等看到。他使用的一样是 db.String
,而他也不能为空,但可以重复 (不设定就没有限制)。email
是电子邮件信箱,基本上我们会在注册的时候发一封信给注册者,然後就再也不会用到了,这就是 Flask-Mail 在这系列唯一的戏份。没什麽好怀疑地,他是字串,然後不能重复,也不能为空。introduction
是存使用者的自我介绍用的,毕竟我们的主题是部落格系统,让作者可以自我介绍应该十分合理。这边就不做限制,可以为空也可以跟别人一样,虽然在正常情况下要跟别人一样还蛮不容易的。is_admin
是用来说明此使用者是不是管理员的,所以他使用的是 db.Boolean
,也就是 True
或是 False
,他不能为空,然後当然可以重复,最後他有一个 default=False
,这代表没有设定的时候他就会自动当他是 False
。register_time
是用来存注册时间。他使用的是 db.Datetime
这个资料库型别,这个型别是用来对付 python 的 datetime.datetime
,所以我们後面的 default
是 datetime.datetime.now
。如果要用 datetime.date
的话,他搭配的资料型别是 db.Date
。这里要特别注意他传入的是 datetime.datetime.now
这个函式,而非 datetime.datetime.now()
。posts
和 comments
用到了 db.relationship
,他分别是和在下两个 table 里面会看到的 posts.id
和 comments.id
有关联,简单来说我们可以用他找到这个使用者底下全部的文章和留言。看完有哪些栏位後,来看看 __init__
这个函式。我们在新增一个使用者的时候,会先用这个 class 造出一个真实的使用者,然後再把造出来的使用者物件加入资料库,所以我们需要这个 __init__
来做初始化,把传入的值一个一个对应到 self
里面。在这里我把 is_admin
直接预设成 False
,所以这样上面的 is_admin
其实就不需要 default=False
了。这里比较特别的是我们用的 generate_password_hash
这个函式,他当然是需要引入的,也就是在最前面的部分。他会把使用者输入的明文密码杂凑,然後存入资料库,然後需要检查的时候就用一起引入的 check_password_hash
来检查,但这边我用另外一个函式 check_password
来包装一下,外面会用到的话就直接套用这个函式即可。
接下来就来看看剩下两个。
class Posts(db.Model):
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
title = db.Column(db.String, nullable=False, unique=True)
description = db.Column(db.String)
content = db.Column(db.String, nullable=False)
comments = db.relationship("Comments")
time = db.Column(
db.DateTime, default=datetime.datetime.now, nullable=False
)
def __init__(self, author_id, title, description, content):
self.author_id = author_id
self.title = title
self.description = description
self.content = content
class Comments(db.Model):
__tablename__ = "comments"
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
post_id = db.Column(db.Integer, db.ForeignKey("posts.id"), nullable=False)
content = db.Column(db.String, nullable=False)
time = db.Column(
db.DateTime, default=datetime.datetime.now, nullable=False
)
def __init__(self, author_id, post_id, content):
self.author_id = author_id
self.post_id = post_id
self.content = content
我们分别说明他们两个的各栏位用途,跟刚刚类似的会直接跳过,首先是 posts
,他会储存全部的文章资讯,包括标题、内容、作者等等。
author_id
是这篇文章的作者的使用者 ID (users.id
),他是把 users.id
当外键 (foreign key),然後让 users.posts
可以存取到 posts 这个 table 的资料。而他当然不能为空 (如果使用者遭到删除,文章和留言也会跟着删除)。title
是这篇文章的标题,不能为空,也不能重复。description
是这篇文章的小标,有没有都没差。content
是内文,当然不能为空。comments
又是一个 relationship,他和下面的 comments
有关连,所以可想而知,comments
里面一定也有一个跟这个 table 有关的外键。最後看到 comments
这个 table,他是用来储存留言资讯的。
author_id
跟上面 posts
里面的一样,就是用 users.id
作为外键让 users.comments
可以存取这个 table。当然也不能为空。post_id
就是用到刚刚 posts
那个 table 的 relationship,让 posts.comments
可以存取到其下的留言。在文章的最後,我们来看一下这个 relationship
会发生甚麽事。我们用刚刚的三个 table 举例,但不会有范例程序码。
假设现在有一个使用者叫做 user1
,他是一个从资料库捞出来的实体,也就是说我们可以存取 user1.username
、user1.email
,当然,我们也可以存取 user1.posts
、user1.comments
。
再假设他有两篇文章,标题分别是 article1
、article2
,又有两则留言,内容分别是 comment1
、comment2
。
这时候我们再回到 user1
,他的 user1.posts
是一个长度为 2 的 list,内容是那两个文章,我们可以使用 user1.posts[0]
来直接抓出第一篇文章的实体,所以我们就可以用这个实体来存取这篇文章的资料,user1.posts[0].title
就会是 article1
,同样地,user1.comments[1].content
就会是 comment2
。
unable to create autoincrementing primary key with flask-sqlalchemy
Column and Data Types
SQLAlchemy default DateTime
flask-sqlalchemy Quickstart
外键 Foreign key (FK) 是什麽?
Day 32 资料库正规化 (一 ~ 三)
=x= 🌵 建立 Yacht Manager - Content Page 後台页面。 Home 前...
今天开始将进行Python基本语法练习,因大部分语法跟很多程序语言相似,故这个部分将主要以笔记方式注...
LastActivityView 今天来认识这个看就知道是看这台电脑上一步做了啥动作的工具! 调查的...
大家好,来到 IT 铁人赛最後一天了,终於要进入尾声了! 今天我们要整合这三十天来所学到的知识量,...
对於网上可用的服务,通常使用通过ID和密码进行身份验证,以验证使用者是否有权使用该服务。LINE 开...