Rails: buat di asosiasi has_one


100

Hai (pemula Rails besar di sini), saya memiliki model berikut:

class Shop < ActiveRecord::Base
  belongs_to :user
  validates_uniqueness_of :title, :user_id, :message => "is already being used"
end

dan

class User < ActiveRecord::Base
  has_one :shop, :dependent => :destroy
end

Ketika saya akan membuat toko baru, saya mendapatkan kesalahan berikut:

private method `create' called for nil:NilClass

Ini pengontrol saya:

@user = current_user
@shop = @user.shop.create(params[:shop])

Saya telah mencoba variasi yang berbeda dengan membaca panduan dan tutorial di sana-sini, tetapi saya lebih bingung daripada sebelumnya dan tidak dapat membuatnya berfungsi. Bantuan apa pun akan sangat dihargai.


Judul pertanyaan diedit untuk mencerminkan pertanyaan. Duplikat Menggunakan build dengan asosiasi has_one di rel
Marc-André Lafortune

1
Anda juga dapat menggunakan@user.build_shop(params)
ImranNaqvi

Jawaban:


123

Pertama-tama, berikut ini cara melakukan apa yang Anda inginkan:

@user = current_user
@shop = Shop.create(params[:shop])
@user.shop = @shop

Sekarang, inilah mengapa versi Anda tidak berfungsi:

Anda mungkin berpikir bahwa ini mungkin berhasil karena jika Pengguna memiliki has_manyhubungan dengan Toko, @user.shops.create(params[:shop]) akan berhasil. Namun ada perbedaan besar antara has_manyrelasi dan has_onerelasi:

Dengan suatu has_manyrelasi, shopsmengembalikan objek koleksi ActiveRecord, yang memiliki metode yang dapat Anda gunakan untuk menambah dan menghapus toko ke / dari pengguna. Salah satu metode tersebut adalah create, yang membuat toko baru dan menambahkannya ke pengguna.

Dengan has_onerelasi, Anda tidak akan mendapatkan kembali objek koleksi seperti itu, tetapi cukup objek Toko yang dimiliki pengguna - atau nol jika pengguna belum memiliki toko. Karena baik objek Shop maupun nil tidak memiliki createmetode, Anda tidak dapat menggunakan createcara ini dengan has_onerelasi.


Terima kasih atas jawaban Anda, sepp2k. Sekarang saya mengerti mengapa kode saya tidak dapat berfungsi.
Neko

118
Anda juga bisa menggunakan @user.create_shop(params[:shop]). Lihat metode yang ditambahkan oleh has_one .
Nates

Jawaban yang dipilih berfungsi, tetapi solusi @nates juga berfungsi. +1 untuk Anda berdua.
nfriend21

+1 untuk jawaban karena saya bertanya-tanya hal yang sama, +1 untuk jawaban untuk menjelaskan mengapa ini dan +1 untuk komentar untuk memberikan solusi terbaik.
membagi

224

Cara yang lebih ringkas untuk melakukannya adalah dengan:

@user.create_shop(params[:shop])

Lihat metode yang ditambahkan oleh has_one di panduan Ruby on Rails.


6
Ini jelas merupakan pendekatan yang lebih baik
Magnum

7
Berhati-hatilah jika Anda membuat toko lebih dari sekali, itu akan menghapus toko sebelumnya. Misalnya jika Anda menjalankannya @user.create_shop(params[:shop_one_info])akan membuat shop_one, TETAPI jika dijalankan @user.create_shop(params[:shop_two_info])itu akan menghapus toko pertama dan membuat yang kedua.
ecoding5

Komentar di atas tentang menghapus toko sebelumnya adalah untuk Rails 3.2.18, tidak tahu tentang versi yang lebih baru. Tidak dapat mengedit komentar setelah 5 menit -_-
ecoding5

Menemukan solusi, saya tidak menetapkan keunikan pada model terkait, jadi pastikan Anda melakukan seperti yang diatur dalam model Toko contoh ini.
ecoding5

Anda juga dapat menggunakan@user.build_shop(params)
ImranNaqvi

7

Dua cara lagi jika Anda mau, savebukan create:

shop = @user.build_shop
shop.save

shop = Show.new
shop.user = @user
shop.save

1

Hanya untuk menambah jawaban di atas -

@user.create_shop(params[:shop])

Sintaks di atas membuat rekaman baru tetapi kemudian menghapus rekaman serupa yang sudah ada.

Alternatifnya, jika Anda tidak ingin memicu delete callback

Shop.create(user_id: user.id, title: 'Some unique title')

Utas ini mungkin bisa membantu. Klik disini

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.