You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 5.1外键命名与创建指南:Expense外键关联失败排查

解答你的Rails开发问题

Hey there! Let's break down your two questions, starting with the immediate issue that's causing your expense records to have null foreign keys—since that's blocking your progress right now.


问题2:为什么seeds.rb生成的Expense外键为null?

You've got two key issues here, let's fix them step by step:

1. 你传递的是对象而非ID值

In your seeds.rb, you're passing Vendor.first directly to vendor_id, but vendor_id is an integer column that expects the ID number of the vendor, not the entire Vendor object. Same goes for expensetype_id.

Fix your seeds code to use .id:

Expense.create!(
  amount: 500, 
  date: Date.today, 
  vendor_id: Vendor.first.id,  # Add .id here
  expensetype_id: ExpenseType.second.id,  # And here
  note: "This is the first expense!"
)

2. 外键命名不符合Rails约定

Looking at your schema and model:

  • Your model has belongs_to :expensetype, but your class is named ExpenseType (camel case). Rails expects the association name to be snake case (expense_type) and the foreign key to be expense_type_id (with an underscore), but your schema uses expensetype_id (no underscore). This mismatch means Rails can't automatically map the association to the correct column.

You have two options to fix this:

Option A: Adjust the model to match your existing column (quick fix)

Update your expense.rb to explicitly specify the foreign key:

class Expense < ApplicationRecord
  belongs_to :vendor  # This works because vendor_id matches the convention
  belongs_to :expensetype, foreign_key: 'expensetype_id'  # Add foreign_key here
end

Option B: Rename the column to follow Rails conventions (recommended for long-term)

This will make future development smoother since you'll stick to Rails' defaults.

  1. Generate a migration to rename the column:
    rails generate migration RenameExpensetypeIdToExpenseTypeIdOnExpenses
    
  2. Open the generated migration file and add:
    def change
      rename_column :expenses, :expensetype_id, :expense_type_id
    end
    
  3. Run the migration:
    rails db:migrate
    
  4. Update your expense.rb to use the correct association name:
    class Expense < ApplicationRecord
      belongs_to :vendor
      belongs_to :expense_type  # Snake case to match ExpenseType class
    end
    
  5. Don't forget to update your seeds.rb to use the new column name:
    Expense.create!(
      amount: 500, 
      date: Date.today, 
      vendor_id: Vendor.first.id,
      expense_type_id: ExpenseType.second.id,  # Updated column name
      note: "This is the first expense!"
    )
    

Side note: Why has_one didn't work

Switching to has_one was incorrect here—belongs_to is the right choice because an Expense belongs to a single Vendor and a single ExpenseType. The inverse would be adding has_many :expenses to your Vendor and ExpenseType models (if you want to access all expenses for a vendor/type later).


问题1:Rails 5.1中如何命名与创建外键?

Rails relies heavily on conventions to reduce boilerplate—here's how it works for foreign keys:

Naming Rules

  • For a belongs_to :model_name association, the default foreign key is model_name_id (snake_case of the model class name).
    • Example: belongs_to :vendor → foreign key vendor_id
    • Example: belongs_to :expense_type → foreign key expense_type_id
  • If you need to use a non-standard foreign key (like your original expensetype_id), specify it with the foreign_key option in the association (as shown in Option A above).

Creating Foreign Keys

There are a few ways to set up foreign keys in Rails 5.1:

1. Generate a model with associations (easiest)

When creating a new model, specify associations using the :references type, and Rails will handle the rest:

rails generate model Expense amount:integer date:date vendor:references expense_type:references note:string

This generates a migration that:

  • Adds vendor_id and expense_type_id integer columns to the expenses table
  • Adds indexes for these columns
  • Adds database-level foreign key constraints (if your database supports it—like PostgreSQL; SQLite may require extra configuration)

2. Add a foreign key to an existing model

If you need to add an association to an existing model, generate a migration with the :references type:

rails generate migration AddVendorIdToExpenses vendor:references

Run the migration with rails db:migrate to add the column, index, and foreign key constraint.

3. Manually add a database foreign key constraint

If you already have the foreign key column but want to add a database-level constraint (to enforce referential integrity), generate a blank migration:

rails generate migration AddForeignKeyConstraintsToExpenses

Then add the constraints in the migration:

def change
  add_foreign_key :expenses, :vendors
  add_foreign_key :expenses, :expense_types
end

Run rails db:migrate to apply the changes.


内容的提问来源于stack exchange,提问作者Jay Wharton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:54:08