简体   繁体   中英

Swift 5.2 Xcode 11.4 Vapor 4.0.0 how should I code the PK in my model?

My Package.swift file looks like:

.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0-rc"),
.package(url: "https://github.com/vapor/fluent.git", from: "4.0.0-rc"),
.package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0-rc")

My config.swift file looks like:

app.databases.use(.postgres(
    hostname: Environment.get("DATABASE_HOST") ?? "localhost",
    username: Environment.get("DATABASE_USERNAME") ?? "postgres",
    password: Environment.get("DATABASE_PASSWORD") ?? "secret",
    database: Environment.get("DATABASE_NAME") ?? "mydb2"
), as: .psql)

My model looks like:

import Fluent
import Vapor


final class Complaint: Model, Content {

    static let schema = "cw_complaint5"

    @ID(key: .id)
    var id: UUID?

    @Field(key: "issue_description")
    var issue_description: String

    // etc.
    init() { }
}

In Xcode, the project builds OK, and this GET route runs OK:

http://localhost:8080/complaint

But when I run the POST route, I get an error in the response body:

{
    "error": true,
    "reason": "Value of type 'UUID' required for key 'id'."
}

In Vapor 3, the POST route worked fine for both inserting a new row (omit the ID in the JSON request body) and updating an existing row. The table description on PostgreSQL 11.3 looks like:

mydb2=# \d+ cw_complaint5
                                              Table "public.cw_complaint5"
        Column        |          Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
----------------------+-------------------------+-----------+----------+---------+----------+--------------+-------------
 id                   | integer                 |           | not null |         | plain    |              | 
 row                  | smallint                |           |          |         | plain    |              | 
 document_number      | integer                 |           | not null |         | plain    |              |
...etc...
county_id            | integer                 |           |          |         | plain    |              | 
 city_id              | integer                 |           |          |         | plain    |              | 
 operator_id          | integer                 |           |          |         | plain    |              | 
Indexes:
    "cw_complaint5_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "cw_complaint5_row_check" CHECK ("row" >= 0)
Foreign-key constraints:
    "cw_complaint5_city_id_fkey" FOREIGN KEY (city_id) REFERENCES cw_city5(id)
    "cw_complaint5_county_id_fkey" FOREIGN KEY (county_id) REFERENCES cw_county5(id)
    "cw_complaint5_operator_id_fkey" FOREIGN KEY (operator_id) REFERENCES cw_operator5(id)

(END)

The request body of the failiing POST request (update existing row) looks like:

{"id":729,"issue_description":"test","document_number":99,"api_state_code":88,"api_county_code":11,"section":22,"city_id":51,"county_id":56,"operator_id":4415}

Your issue here is that the Complaint model's ID type is UUID , but in your POST request body, you are passing in an Int value. Since you are migrating an existing project that already has a database defined with integer IDs, the best solution for this is to change your model's ID type.

Because Fluent 4 attempts to support all databases, by default it only supports UUID ID types. You can have a custom ID type, like this:

@ID(custom: "id", generatedBy: .database) var id: Int?

This will allow your model to work with your current database table structure and take in integer IDs in your POST request body.

Your model will look like this in the end:

final class Complaint: Model, Content {

    static let schema = "cw_complaint5"

    @ID(custom: "id", generatedBy: .database)
    var id: Int?

    @Field(key: "issue_description")
    var issue_description: String

    // etc.
    init() { }
}

Solution: serialize the table in PostgreSQL:

mydb2=# ALTER TABLE cw_complaint5 DROP COLUMN id;

mydb2=# ALTER TABLE cw_complaint5 ADD COLUMN id SERIAL PRIMARY KEY;

Then to insert a row (in xcode 11.4 vapor 4 fluentpostgresql) remove id from your POST request json eg:

{"section":1,"operator_id":4415 ... etc.}

To update an existing row, include the id in the json. The following in routes.swift handles both insert and update:

app.post("complaint") { req -> EventLoopFuture<Complaint> in
    let complaint = try req.content.decode(Complaint.self)
    if let id = complaint.id {
        print("60p \(id)") // Optional(6005)
    }
    return complaint.save(on: req.db)
        .map { complaint }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM