0x00 Chapter 9: Parent-Child Relationships
5.Setting up the relationship
Because a
user
owns each
acronym
, you add a
user
property to the
acronym
.
a. open Acronym.swift
add a new property after
var long: String
@Parent(key: "userID")
var user: User
Replace the initializer
init(id: UUID? = nil, short: String, long: String, userID: User.IDValue) {
self.id = id
self.short = short
self.long = long
self.$user.id = userID
}
b. open CreateAcronym.swift
Before
.create()
add the following line
.field("userID", .uuid, .required)
c. open AcronymsController.swift
add the following code At the bottom of file
struct CreateAcronymData: Content {
let short: String
let long: String
let userID: UUID
}
update the body of
createHandler(_:)
:
func createHandler(_ req: Request) throws -> EventLoopFuture<Acronym> {
//let acronym = try req.content.decode(Acronym.self)
let data = try req.content.decode(CreateAcronymData.self)
let acronym = Acronym(short: data.short, long: data.long, userID: data.userID)
return acronym.save(on: req.db).map { acronym }
}
update the body of
updateHandler(_:)
:
func updateHandler(_ req: Request) throws -> EventLoopFuture<Acronym> {
//let updatedAcronym = try req.content.decode(Acronym.self)
let updateData = try req.content.decode(CreateAcronymData.self)
return Acronym.find(req.parameters.get("acronymID"), on: req.db)
.unwrap(or: Abort(.notFound)).flatMap { acronym in
acronym.short = updateData.short
acronym.long = updateData.long
acronym.$user.id = updateData.userID
return acronym.save(on: req.db).map {
acronym
}
}
}
6.reset the database
To add the
new column
to the table, you must
delete
the database so
Fluent
will run the migration again.
如果数据库在运行,先停止
打开终端:
docker stop postgres
删除数据库:
docker rm postgres
重新创建数据库:
docker run --name postgres \
-e POSTGRES_DB=vapor_database \
-e POSTGRES_USER=vapor_username \
-e POSTGRES_PASSWORD=vapor_password \
-p 5432:5432 \
-d postgres
因为数据库被删除了
所以数据需要重新生成
参考前面几遍文章
提交
acronym
数据:
url:
http://127.0.0.1:8080/api/acronyms
method:
POST
parameters:
{"short": "MMD", "long":"么么哒","userID":"A1B2C3D4"}
userID
是随便写的
后面加了外键约束后,就不能随便写了
提交
user
数据:
url:
http://127.0.0.1:8080/api/users
method:
POST
parameters:
{"name": "老三", "username":"张三"}
7.Querying the relationship
Getting the parent
open AcronymsController.swift
add a
new
route handler after
sortedHandler(_:)
:
func getUserHandler(_ req: Request) throws -> EventLoopFuture<User> {
Acronym.find(req.parameters.get("acronymID"), on: req.db)
.unwrap(or: Abort(.notFound))
.flatMap { acronym in
acronym.$user.get(on: req.db)
}
}
Register the route handler at the
end
of
boot(routes:)
acronymsRoutes.get(":acronymID", "user", use: getUserHandler)
根据
acronym
的
ID
查询
user
url:
http://127.0.0.1:8080/api/acronyms/F3320C03-8570-443F-A8AB-071681470DA4/user
method:
GET
parameters:
无
通过浏览器直接访问
返回数据,格式化后:
{
"id": "0DFD7B3A-F38D-43AA-8DD5-26B71E4FE3D0",
"username": "张三",
"name": "老三"
}
Getting the children
open User.swift
add a
new
property below
var username: String
@Children(for: \.$user)
var acronyms: [Acronym]
open UsersController.swift
add a
new
route handler after
getHandler(_:)
:
func getAcronymsHandler(_ req: Request) throws -> EventLoopFuture<[Acronym]> {
User.find(req.parameters.get("userID"), on: req.db)
.unwrap(or: Abort(.notFound))
.flatMap { user in
user.$acronyms.get(on: req.db)
}
}
Register the route handler at the
end
of
boot(routes:)
:
usersRoute.get(":userID", "acronyms", use: getAcronymsHandler)
根据
user
的
ID
查询
acronym
url:
http://127.0.0.1:8080/api/users/0DFD7B3A-F38D-43AA-8DD5-26B71E4FE3D0/acronyms
method:
GET
parameters:
无
通过浏览器直接访问
返回数据:
[
{
"id": "F3320C03-8570-443F-A8AB-071681470DA4",
"short": "MMD",
"long": "么么哒",
"user": {
"id": "0DFD7B3A-F38D-43AA-8DD5-26B71E4FE3D0"
}
}
]
8.Foreign key constraints
外键约束
describe a
link
between
two
tables
Using
foreign key constraints
has a number of
benefits
:
- It ensures you
create acronyms with users that can’t
existdon’t
- You
delete users until you’ve can’t
all their acronyms.deleted
- You
delete the user table until you’ve can’t
the acronym table.deleted
9.Foreign key constraints are set up in the migration.
Open
CreateAcronym.swift
,
and replace
.field("userID", .uuid, .required)
with the following
.field("userID", .uuid, .required, .references("users", "id"))
Finally, because you’re linking the acronym’s
userID
property to the
User
table
you must
create
the
User
table
first
In
configure.swift
, move the
User
migration to before the
Acronym
migration:
app.migrations.add(CreateUser())
app.migrations.add(CreateAcronym())
最后再重新操作一遍:
停止数据库:
docker stop postgres
删除数据库:
docker rm postgres
重新创建数据库:
docker run --name postgres \
-e POSTGRES_DB=vapor_database \
-e POSTGRES_USER=vapor_username \
-e POSTGRES_PASSWORD=vapor_password \
-p 5432:5432 \
-d postgres
提交
acronym
时,如果
userID
随便写
比如把
userID
最后一位改一下
会导致错误:
violates foreign key constraint
违反外键约束
{
"error": true,
"reason": "server: insert or update on table \"acronyms\" violates foreign key constraint \"acronyms_userID_fkey\" (ri_ReportViolation)"
}