Rails: Menggunakan build dengan asosiasi has_one di rails

143

Dalam contoh ini, saya membuat a userdengan tidak profile, kemudian membuat profileuntuk pengguna itu. Saya mencoba menggunakan build dengan has_oneasosiasi tetapi itu meledak. Satu-satunya cara saya melihat ini berfungsi adalah menggunakan has_many. The userseharusnya hanya memiliki paling banyak satu profile.

Saya sudah mencoba ini. Saya sudah:

class User < ActiveRecord::Base
  has_one :profile
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

Tetapi ketika saya melakukannya:

user.build_profile 

Saya mendapatkan kesalahan:

ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'profiles.user_id' in 'where clause': SELECT * FROM `profiles` WHERE (`profiles`.user_id = 4)  LIMIT 1

Apakah ada cara di rel untuk memiliki asosiasi 0 atau 1?

espinet
sumber
apa sebenarnya yang kamu coba? bisakah Anda mengirim beberapa kode?
Ju Nogueira

Jawaban:

359

The buildMetode tanda tangan yang berbeda untuk has_onedan has_manyasosiasi.

class User < ActiveRecord::Base
  has_one :profile
  has_many :messages
end

Sintaks build untuk has_manyasosiasi:

user.messages.build

Sintaks build untuk has_oneasosiasi:

user.build_profile  # this will work

user.profile.build  # this will throw error

Baca dokumentasihas_one asosiasi untuk lebih jelasnya.

Harish Shetty
sumber
28
Sintaks yang berbeda untuk has_one selalu menangkap saya ... sialan!
Galaxy
11
Sangat lucu bagaimana jawaban berperingkat teratas dan diterima di sini menjawab pertanyaan yang berbeda dari yang ditanyakan OP.
Ajedi32
Seharusnya jika pengguna milik profil (artinya tabel pengguna memiliki foreign_key profile_id di tabelnya) maka juga membangun profil untuk pengguna akan berfungsi seperti yang disebutkan di atas yaitu tetapi untuk tindakan baru hanya user.build_profile untuk mengedit user.build_profile if user.profile.nil? dan jika Anda ingin membangun profil saat membuat pengguna kemudian menulis accepts_nested_attributes_for :profileini di Model pengguna. dan dalam bentuk mana pengguna sedang dibuat, tulis <%= f.simple_fields_for :profile do |p| %>ini dan lanjutkan.
semangat
tetapi mengapa perilaku yang berbeda ini disimpan untuk has_one atau has_many? Akan ada beberapa alasan saat mendesain, saya pikir dan harapkan.
ingin tahu
@ Ajedi32 jawabannya cocok dengan judul pertanyaan tetapi tidak dengan tubuh. Mengingat bahwa ini ( build_<association>) adalah perilaku yang cukup aneh dan tidak terduga di Rails, ada lebih banyak orang yang mencari jawaban ini daripada jawaban pertanyaan yang sebenarnya, jika Anda tahu apa yang saya maksud.
Max Williams
19

Perhatikan baik-baik pesan kesalahan. Ini memberi tahu Anda bahwa Anda tidak memiliki kolom yang diperlukan user_iddalam tabel profil . Mengatur hubungan dalam model hanyalah sebagian dari jawabannya.

Anda juga perlu membuat migrasi yang menambahkan user_idkolom ke tabel profil. Rails mengharapkan ini ada di sana dan jika tidak, Anda tidak dapat mengakses profil.

Untuk informasi lebih lanjut silakan lihat tautan ini:

Dasar-Dasar Asosiasi

sosborn
sumber
1
Saya baru saja menemukan masalah saya. Buku yang saya pelajari tidak menjelaskan penciptaan kunci asing dengan baik. Saya membuat migrasi baru yang menambahkan kunci asing ke model saya. Terima kasih.
espinet
Apakah Anda perlu membuat kolom sendiri setiap kali? Saya punya ide bahwa ini terjadi secara otomatis. Saya tidak tahu dari mana saya mendapatkan ide itu.
Rimian
Anda dapat menambahkan kolom saat membuat model menggunakan baris perintah, sesuatu seperti rails g model profile user:references:index address:string bio:text.
duykhoa
1

Tergantung pada kasus penggunaan, akan lebih mudah untuk membungkus metode ini dan secara otomatis membangun asosiasi ketika tidak ditemukan.

old_profile = instance_method(:profile)
define_method(:profile) do
  old_profile.bind(self).call || build_profile
end

sekarang memanggil #profilemetode akan mengembalikan profil yang terkait atau membuat contoh baru.

sumber: Ketika monyet menambal suatu metode, dapatkah Anda memanggil metode yang diganti dari implementasi yang baru?

Shiyason
sumber
1
di rel saat ini (diuji pada 6.0.2.2) Anda dapat menyederhanakan ini: def profile; super || build_profile; end.
glasz
-14

Itu harus a has_one. Jika buildtidak berfungsi, Anda bisa menggunakan new:

ModelName.new( :owner => @owner )

sama dengan

@owner.model_names.build
Karl
sumber
11
Ini tidak sama: jika Anda membuat model_name baru dengan build, ketika @owner disimpan maka model_name baru akan disimpan juga. Jadi, Anda bisa menggunakan build untuk membuat orang tua dan anak-anak yang akan diselamatkan bersama. Ini tidak terjadi jika Anda membuat model_name dengan .new
Max Williams