مشکل در associationsها

سلام دارم یه دوره از آموزشهای ActiveRecord رو میگذرونم. کسی که داره آموزش میده از ریلز ۴ استفاده میکنه و من ریلز ۵.
من سه جدولِ customers, employees و جدولِ time_entries دارم که کارش رابط شدن بین دوتا جدوله درست کردم.

کدهای migration به این صورتن:

class CreateCustomers < ActiveRecord::Migration[5.0]
  def change
    create_table :customers do |t|
      t.string :name
      t.string :about
      t.string :email
      t.integer :balance
      t.timestamps
    end
  end
end
class CreateTimeEntries < ActiveRecord::Migration[5.0]
  def change
    create_table :time_entries do |t|
      t.float :time
      t.belongs_to :customer
      t.belongs_to :employee
      t.timestamps
    end
  end
end
class CreateEmployees < ActiveRecord::Migration[5.0]
  def change
    create_table :employees do |t|
      t.string :name
      t.string :email
      t.timestamps
    end
  end
end

و کدهای modelها به این صورت:

class Customer < ApplicationRecord
  has_many :time_entries
end
class Employee < ApplicationRecord
  has_many :time_entries
end
class TimeEntry < ApplicationRecord
  belongs_to :employee
  belongs_to :customer
end

اما مشکل جایی شروع میشه که میخوام این دستور رو توی کنسول ریلز اجرا کنم که با خطاهای زیر مواجه میشه:

2.3.1 :002 > TimeEntry.create(time: 1.2, customer_id: 1)
   (0.3ms)  BEGIN
  Customer Load (0.8ms)  SELECT  `customers`.* FROM `customers` WHERE `customers`.`id` = 1 LIMIT 1
   (0.3ms)  ROLLBACK
 => #<TimeEntry id: nil, time: 1.2, customer_id: 1, employee_id: nil, created_at: nil, updated_at: nil>end

از قبل هم یه Customer درست کردم پس مشکلی هم از او ن لحاظ نیست. نمیدونم کجای قضیه رو اشتباه رفتم واسه همین کلی اسم تاپیک رو زدم associations.
ممنون.

سلام,
1- برای اینکه ببینید چه اتفاقی میفته از متد !create که ورژن صریح create است استفاده کنید. (explicit create)


errot:
ActiveRecord::RecordInvalid: Validation failed: Employee must exist
ارور میگه Employee باید وجود داشته باشه

2- ایجاد اتصال با id را به ActiveRecord بسپارید و خودتون customer_id رو وارد نکنید.

conclusion:
به مثال زیر توجه کنید

1 پسندیده

خیلی خب طبق چیزی که گفتی:
پروژه رو دوباره از اول درست کردم، ولی ایندفعه اون بخش ‍‍‍belongs_to :employee و ‍‍‍‍belongs_to :customer رو توی migration نیوردم و از طریق همون روابط توی مدلهای جلو رفتم. دوتا دستور find.rb رو زدم.
و وقتی محتویات فایل example.rb رو اجرا کردم با خطای زیر مواجه شدم:

customer = Customer.create!(name: 'Farbod', email: '[email protected]')
   (0.3ms)  BEGIN
  SQL (0.7ms)  INSERT INTO `customers` (`name`, `email`, `created_at`, `updated_at`) VALUES ('Farbod', '[email protected]', '2016-10-15 14:49:17', '2016-10-15 14:49:17')
   (82.6ms)  COMMIT
 => #<Customer id: 2, name: "Farbod", email: "[email protected]", balance: nil, created_at: "2016-10-15 14:49:17", updated_at: "2016-10-15 14:49:17"> 
2.3.1 :002 > employee = Employee.create!(name: 'Toomaj', email: '[email protected]')
   (0.2ms)  BEGIN
  SQL (0.4ms)  INSERT INTO `employees` (`name`, `email`, `created_at`, `updated_at`) VALUES ('Toomaj', '[email protected]', '2016-10-15 14:49:22', '2016-10-15 14:49:22')
   (100.9ms)  COMMIT
 => #<Employee id: 2, name: "Toomaj", email: "[email protected]", created_at: "2016-10-15 14:49:22", updated_at: "2016-10-15 14:49:22"> 
2.3.1 :003 > TimeEntry.create!(time: 1.2, customer: customer, employee: employee)
ActiveModel::MissingAttributeError: can't write unknown attribute `customer_id`

الان scheme به این صورته:

  create_table "customers", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.string   "name"
    t.string   "email"
    t.integer  "balance"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "employees", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.string   "name"
    t.string   "email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "time_entries", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.float    "time",       limit: 24
    t.datetime "created_at",            null: false
    t.datetime "updated_at",            null: false
  end

من كى گفتم پروژه رو از اول درست كنيد يا تقير بدين!!؟
چيزهايى كه عرض كردم رو بايد طبق همون پروژه توى ترمينال اجرا كنيد و نتيجه رو برسى كنيد، همين.

وقتى belongs_to رو از migration پاك كردين، ديگه foreign_key ها اصلا وجود ندارن كه بخواى relation ايجاد كنيد.

دوباره انجام دادم و مشکل حل شد، میشه یه توضیح بدی که منظور از خطای ActiveRecord::RecordInvalid: Validation failed: Employee must exist چیه و چه الزامی برای دادن هردو پارامتر Employee و Customer واسه موقع ساخت TimEntry.Create هست؟

ممنون :slight_smile:

1 پسندیده

همنطور که ميبينيد ارور در لایه ActiveRecord است.
متد belongs_to در مدل TimeEntry در پشت صحنه validation ای را ایجاد کرده که presence یا حضور object مربوطه رو برسی میکنه.
در مورد شما Customer و Employee باید وجود داشته باشند.
حالا میتونید متد های belongs_to را برای آزمایش این داستان پاک کنید و نتیجه رو ببینید, مثلا belongs_to: customer را پاک کنید و بدون customer یک رکورد TimeEntry درست کنید.
یادتون باشه هر بار که مدل را تقیر میدین rails console را دوباره اجرا کنید یا فرمان زیر رو بزنید که reload بشه و تقیرات را بارگذاری کنه

در پایان:
بحثمون فقط در مورد belongs_to در مدل ها بود و با belongs_to در migration ها کاری نداریم چون دومی فقط foreign_key درست کرده + index

1 پسندیده