Rails update_attributes tanpa menyimpan?

386

Apakah ada alternatif untuk update_attributes yang tidak menyimpan catatan?

Jadi saya bisa melakukan sesuatu seperti:

@car = Car.new(:make => 'GMC')
#other processing
@car.update_attributes(:model => 'Sierra', :year => "2012", :looks => "Super Sexy, wanna make love to it")
#other processing
@car.save

BTW, saya tahu saya bisa @car.model = 'Sierra', tetapi saya ingin memperbarui semuanya dalam satu baris.

tybro0103
sumber
apa maksudmu "tidak menyimpan catatan"?
Anatoly
update_attributes menyimpan model DB. Saya bertanya-tanya apakah ada metode serupa yang tidak.
tybro0103
3
atribut metode non-destruktif. Lihat API untuk detailnya
Anatoly
3
Anda dapat menggunakan update_column (nama, nilai) Memperbarui atribut tunggal objek, tanpa memanggil save. 1. Validasi dilewati. 2. Panggilan balik dilewati. 3. updated_at / updated_on kolom tidak diperbarui jika kolom itu tersedia. apidock.com/rails/ActiveRecord/Persistence/update_column
Antoine
10
Untuk 3.1+, gunakan assign_attributes apidock.com/rails/ActiveRecord/Base/assign_attributes
elado

Jawaban:

597

Saya percaya apa yang Anda cari assign_attributes.

Ini pada dasarnya sama dengan update_attributes tetapi tidak menyimpan catatan:

class User < ActiveRecord::Base
  attr_accessible :name
  attr_accessible :name, :is_admin, :as => :admin
end

user = User.new
user.assign_attributes({ :name => 'Josh', :is_admin => true }) # Raises an ActiveModel::MassAssignmentSecurity::Error
user.assign_attributes({ :name => 'Bob'})
user.name        # => "Bob"
user.is_admin?   # => false
user.new_record? # => true
Ajedi32
sumber
Contoh Anda agak menyesatkan karena Anda belum menempelkan baris ini dari model attr_accessible :is_admin, :as => :admin:;)
Robin
@Robin Atau hanya: attr_protected :is_admin. Atau: attr_accessible :nameIntinya adalah bahwa dalam contoh ini,: is_admin dilindungi. Saya juga harus mencatat bahwa upaya untuk secara massal menetapkan atribut yang dilindungi dengan .assign_attributesmemang meningkatkan ActiveModel::MassAssignmentSecurity::Error, meskipun itu tidak ditampilkan dalam contoh.
Ajedi32
Ya tapi baris saya dari dokumen yang Anda tautkan. Saya hanya mengatakan Anda harus menyalin / menempelkan seluruh contoh. Tapi ya, Anda bisa mengatakan bahwa itu dilindungi.
Robin
@Robin Saya akan memperbarui contoh agar sedikit lebih spesifik. Contoh dalam dokumen juga agak menyesatkan, karena tidak menyebutkan yang user.assign_attributes({ :name => 'Josh', :is_admin => true })memunculkan pesan kesalahan dan tidak benar-benar mengatur properti nama pengguna.
Ajedi32
7
assign_attributes tersedia dari Rails 3.1 dan seterusnya, jadi Anda tidak dapat menggunakannya jika Anda masih menjalankan Rails versi lama.
Haegin
174

Anda dapat menggunakan assign_attributesatau attributes=(mereka sama)

Perbarui metode cheat sheet (untuk Rails 6):

  • update= assign_attributes+save
  • attributes= = alias dari assign_attributes
  • update_attributes = usang, alias dari update

Sumber:
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/persistence.rb
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_assignment .rb

Lembar cheat lain:
http://www.davidverhasselt.com/set-attributes-in-activerecord/#cheat-sheet

Yarin
sumber
1
Jelas dan pendek. Terima kasih.
freemanoid
1
dalam kasus .attributes = val, jika model Anda has_one dan accepts_nested_attributes_for model lain, meneruskan that_model_attributes (tanpa id) akan menghapus model has_one yang ada, bahkan jika Anda tidak bertahan (mis. menyimpan). Tapi assign_attributes tidak berperilaku seperti itu.
ClassyPimp
65

Anda dapat menggunakan metode 'atribut':

@car.attributes = {:model => 'Sierra', :years => '1990', :looks => 'Sexy'}

Sumber: http://api.rubyonrails.org/classes/ActiveRecord/Base.html

atribut = (new_attributes, guard_protected_attributes = true) Memungkinkan Anda untuk mengatur semua atribut sekaligus dengan mengirimkan hash dengan kunci yang cocok dengan nama atribut (yang lagi-lagi cocok dengan nama kolom).

Jika guard_protected_attributes benar (default), maka atribut sensitif dapat dilindungi dari bentuk penugasan massal ini dengan menggunakan makro attr_protected. Atau Anda dapat menentukan atribut mana yang dapat diakses dengan makro attr_accessible. Maka semua atribut yang tidak termasuk di dalamnya tidak akan diizinkan untuk ditetapkan secara massal.

class User < ActiveRecord::Base
  attr_protected :is_admin
end

user = User.new
user.attributes = { :username => 'Phusion', :is_admin => true }
user.username   # => "Phusion"
user.is_admin?  # => false

user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false)
user.is_admin?  # => true
bruno077
sumber
7

Untuk penetapan nilai secara massal ke model ActiveRecord tanpa menyimpan, gunakan salah satu assign_attributesatauattributes= metode . Metode ini tersedia di Rails 3 dan yang lebih baru. Namun, ada perbedaan kecil dan yang terkait dengan versi gotcha yang harus diperhatikan.

Kedua metode mengikuti penggunaan ini:

@user.assign_attributes{ model: "Sierra", year: "2012", looks: "Sexy" }

@user.attributes = { model: "Sierra", year: "2012", looks: "Sexy" }

Perhatikan bahwa tidak ada metode yang akan melakukan validasi atau mengeksekusi panggilan balik; panggilan balik dan validasi akan terjadi ketikasave dipanggil.

Rel 3

attributes=sedikit berbeda dari assign_attributesdalam Rails 3. attributes=akan memeriksa bahwa argumen yang diteruskan ke Hash adalah, dan kembali segera jika tidak; assign_attributestidak memiliki pemeriksaan hash semacam itu. Lihat dokumentasi API Penugasan Atribut ActiveRecord untukattributes= .

Kode tidak valid berikut akan gagal secara diam-diam dengan hanya mengembalikan tanpa menetapkan atribut:

@user.attributes = [ { model: "Sierra" }, { year: "2012" }, { looks: "Sexy" } ]

attributes= akan diam-diam bersikap seolah-olah penugasan itu berhasil, padahal sebenarnya tidak.

Kode yang tidak valid ini akan memunculkan eksepsi ketika assign_attributesmencoba merangkai kunci hash dari array terlampir:

@user.assign_attributes([ { model: "Sierra" }, { year: "2012" }, { looks: "Sexy" } ])

assign_attributesakan memunculkan NoMethodErrorpengecualian untuk stringify_keys, menunjukkan bahwa argumen pertama bukan Hash. Pengecualian itu sendiri tidak terlalu informatif tentang penyebab sebenarnya, tetapi fakta bahwa pengecualian memang terjadi adalah sangat penting.

Satu-satunya perbedaan antara kasus-kasus ini adalah metode yang digunakan untuk penugasan massal: attributes=berhasil dengan diam-diam, dan assign_attributesmenimbulkan pengecualian untuk menginformasikan bahwa telah terjadi kesalahan.

Contoh-contoh ini mungkin tampak dibuat-buat, dan mereka pada tingkat tertentu, tetapi jenis kesalahan ini dapat dengan mudah terjadi ketika mengkonversi data dari API, atau bahkan hanya menggunakan serangkaian transformasi data dan lupa dengan Hash[]hasil akhir.map . Pertahankan beberapa kode 50 baris di atas dan 3 fungsi dihapus dari penugasan atribut Anda, dan Anda punya resep untuk kegagalan.

Pelajaran dengan Rails 3 adalah ini: selalu gunakan assign_attributesbukanattributes= .

Rel 4

Di Rails 4, attributes=hanyalah sebuah alias untuk assign_attributes. Lihat dokumentasi API Penugasan Atribut ActiveRecord untukattributes= .

Dengan Rails 4, metode mana pun dapat digunakan secara bergantian. Kegagalan untuk melewatkan Hash karena argumen pertama akan menghasilkan pengecualian yang sangat membantu:ArgumentError: When assigning attributes, you must pass a hash as an argument.

Validasi

Jika Anda melakukan pra-penerbangan penugasan dalam persiapan untuk save, Anda mungkin juga tertarik untuk memvalidasi sebelum menyimpan. Anda dapat menggunakan valid?dan invalid?metode untuk ini. Keduanya mengembalikan nilai boolean. valid?mengembalikan true jika model yang belum disimpan melewati semua validasi atau false jika tidak. invalid?hanyalah kebalikan darivalid?

valid? dapat digunakan seperti ini:

@user.assign_attributes{ model: "Sierra", year: "2012", looks: "Sexy" }.valid?

Ini akan memberi Anda kemampuan untuk menangani masalah validasi sebelum menelepon save.

Michael Gaskill
sumber