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?


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.


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


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?


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

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.