Home > other >  Rails not returning generated ID for specific model
Rails not returning generated ID for specific model

Time:11-22

I've recently picked up maintenance of a couple of Rails 5.2 apps with a PostgreSQL back end. I'm new to Rails, but I've got a fair bit of experience on the various Microsoft platforms.

I'm trying to add API calls to an existing model. When I attempt to create a new instance, I am not getting the database-generated ID back:

POST /invoices
{ "amount": 12.34 }

Invoice Create (4.0ms)  
  INSERT INTO "invoices" ("amount", "created_at", "updated_at") 
  VALUES ($1, $2, $3)  
  [["amount", 12.34], ["created_at", "..."], ["updated_at", "..."]]

201 Created
{ "id": null, "amount": 12.34 }

Checking the database, the new row is present, with a unique ID.

A different model in the same app generates different SQL and works as expected:

POST /customer
{ "name": "ACME" }

Customer Create (1.4ms)  
  INSERT INTO "customers" ("name", "created_at", "updated_at") 
  VALUES ($1, $2, $3) 
  ** RETURNING "id" **  
  [["name", "ACME"], ["created_at", "..."], ["updated_at", "..."]]

201 Created
{ "id": 111, "name": "ACME" }

I can't see any differences in the two models that explain this behavior. I've checked everything I can think of:

  • routes (via :resources)
  • controller
    • before/after filters
    • strong parameters
    • code in create
  • model
    • neither contains any code
  • schema
    • column definitions are comparable in schema.rb and information_schema.columns

Here's the model and controller for the misbehaving type:

class Invoice < ActiveRecord::Base
end

class InvoiceController < ApplicationController
  def create
    invoice = Invoice.new(invoice_params)
    if invoice.save
      # invoice.id.nil? => true
      render json: invoice, status: :created
    end
  end

  def invoice_params
    params.permit(:amount)
  end
end

# schema.rb
create_table "invoices", id: false, force: :cascade do |t|
  t.serial "id", null: false
  t.float "amount"
  t.datetime "created_at"
  t.datetime "updated_at"
end

And the one that works as expected:

class Customer < ActiveRecord::Base
end

class CustomerController < ApplicationController
  def create
    customer = Customer.new(customer_params)
    if customer.save
      # customer.id.nil? => false
      render json: customer, status: :created
    end
  end

  def customer_params
    params.permit(:name)
  end
end

# schema.rb
create_table "customers", id: :serial, force: :cascade do |t|
  t.string "name"
  t.datetime "created_at"
  t.datetime "updated_at"
end

Replacing new/save with create or create! doesn't change the behavior, so I'm convinced that the problem is somewhere in the model definition or metadata.

Creating the models from rails console has the same result as shown below:

irb(main):001:0> Invoice.create(amount:12.34)
   (0.8ms)  BEGIN
  Invoice Create (1.1ms)  INSERT INTO "invoices" ("amount", "created_at", "updated_at") VALUES ($1, $2, $3)  [["amount", 12.34], ["created_at", "2021-11-19 09:10:33.490117"], ["updated_at", "2021-11-19 09:10:33.490117"]]
   (5.8ms)  COMMIT
=> #<Invoice id: nil, amount: 12.34, created_at: "2021-11-19 09:10:33", updated_at: "2021-11-19 09:10:33">

irb(main):002:0> Customer.create(name: "ACME")
   (0.9ms)  BEGIN
  Customer Create (1.5ms)  INSERT INTO "customers" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "ACME"], ["created_at", "2021-11-19 09:12:50.492927"], ["updated_at", "2021-11-19 09:12:50.492927"]]
   (13.3ms)  COMMIT
=> #<Customer id: 24, name: "ACME", created_at: "2021-11-19 09:12:50", updated_at: "2021-11-19 09:12:50">

Can anyone point me in the right direction?

CodePudding user response:

The difference is that you explicitly declared "id" as a column, and disabled the default primary key "id" declaration / handling.

If you change to:

create_table "invoices", id: :serial, force: :cascade do |t|
  t.float "amount"
  # These are normally created automatically so not sure why they are here
  # t.datetime "created_at"
  # t.datetime "updated_at"
end

it should work.

This answer may also help: https://stackoverflow.com/a/54694863/224837

CodePudding user response:

This is caused by the missing primary key on the tables in question. It looks like these tables might have been created manually at some point early in the project, and have only been written to by external SQL scripts until now.

  • Related