How can I insert multiple records into a database using rails syntax.
INSERT INTO users (email,name) VALUES ('a#ao.in','a'),('b#ao.in','b'),
('c#ao.in','c');
This is how we do it in MySQL. How is this done in Rails?
Check out this blog post: http://www.igvita.com/2007/07/11/efficient-updates-data-import-in-rails/
widgets = [ Widget.new(:title => 'gizmo', :price => 5),
Widget.new(:title => 'super-gizmo', :price => 10)]
Widget.import widgets
Depending on your version of rails, use activerecord-import 0.2.6 (for Rails 3) and ar-extensions 0.9.4 (for Rails 2)
From the author: http://www.continuousthinking.com/tags/arext
While you cannot get the exact SQL that you have there, you can insert multiple records by passing create or new on an array of hashes:
new_records = [
{:column => 'value', :column2 => 'value'},
{:column => 'value', :column2 => 'value'}
]
MyModel.create(new_records)
I use following in my project but it is not proper for sql injection.
if you are not using user input in this query it may work for you
user_string = " ('a#ao.in','a'), ('b#ao.in','b')"
User.connection.insert("INSERT INTO users (email, name) VALUES"+user_string)
Just a use activerecord-import gem for rails 3 or ar-extensions for rails 2
https://github.com/zdennis/activerecord-import/wiki
In Gemfile:
gem "activerecord-import"
In model:
import "activerecord-import"
In controller:
books = []
10.times do |i|
books << Book.new(:name => "book #{i}")
end
Book.import books
This code import 10 records by one query ;)
or
##messages = ActiveSupport::JSON.decode(#content)
#messages = JSON(#content)
#prepare data for insert by one insert
fields = [:field1, :field2]
items = []
#messages.each do |m|
items << [m["field1"], m["field2"]]
end
Message.import fields, items
You can use Fast Seeder to do multiple insert.
In People_controller.rb
# POST people
NAMES = ["Sokly","Nary","Mealea"]
def create
Person.transaction do
NAMES.each do |name|
#name = Person.create(:name => name)
#name.save
end
end
end
Just pass an array of hashs to the create method like this:
User.create([{:email => "foo#com", :name => "foo"}, {:email => "bar#com", :name => "bar"}])
Related
I am looking at posting to an endpoint on Bubble.io using Ruby and they require jsonl (plain text, new-line seperated) instead of JSON.
Is there a way to take a hash and make it jsonl? Something like hash.to_jsonl.
For jsonl (or ndjson), the json need to be formated as single line.
Therefor use to_json method.
require 'json'
group = [{:name => "Tom", :age => 27}, {:name => "Jerry", :age => 37}]
puts group.map { |r| JSON.generate(r) }.join("\n")
This code generates the following:
{"name":"Tom","age":27}
{"name":"Jerry","age":37}
Here is the solution I went with:
group = [{name => "Tom"}, {name => "Jerry"}]
generated = []
group.each do |r|
generated << JSON.generate(r)
end
jsonl_text = generated.join("\n")
For creating I used
contact_ids = params[:ticket_note][:contact_ids].reject(&:blank?)
contact_ids.each do |contact_id|
#ticket_note_recipient = TicketNoteRecipient.new
#ticket_note_recipient.ticket_note_id = #ticket_note.id
#ticket_note_recipient.account_contact_id = contact_id
#ticket_note_recipient.save
end
For Updating I used
contact_ids = params[:ticket_note][:contact_ids].reject(&:blank?)
#d_ticket_note_recipients = TicketNoteRecipient.where(:ticket_note_id =>#ticket_note)
.where.not(:account_contact_id => contact_ids).delete_all
contact_ids.each do |contact_id|
#ticket_note_recipient = TicketNoteRecipient.where(:ticket_note_id =>#ticket_note)
.where.not(:account_contact_id => contact_id).first
if #ticket_note_recipient.blank?
#ticket_note_recipient = TicketNoteRecipient.new
#ticket_note_recipient.ticket_note_id = #ticket_note.id
#ticket_note_recipient.account_contact_id = contact_id
#ticket_note_recipient.save
end
end
How can we do this with fewer database transactions?
It is not possible to insert/modify multiple records with rails out of the box. However you can use activerecord-import gem to do this.
Rails does take arrays by default when creating new objects, like so:
VoteRecord.create(
[
{ :prospect_id => prospect.id, :state => "OH", :election_type => "GE", :election => "2011-11-08", :party => row[82], :participate => participated(row[82]) },
{ :prospect_id => prospect.id, :state => "OH", :election_type => "PR", :election => "2011-09-13", :party => row[81], :participate => participated(row[81]) }
...
]
)
However this would still create n queries, one per insertion, so the only way to achieve this (without writing a SQL query on your own) is by using activerecord-import
If you're really talking about fewer transactions as stated in your question then this is the way to go, wrapping it in a Transaction
ApplicationRecord.transaction do
contact_ids.each do |contact_id|
# ...
#ticket_note_recipient.save
end
end
end
I was wondering if someone could explain how to use will_paginate on an array of objects?
For example, on my site I have an opinion section where users can rate the opinions. Here's a method I wrote to gather the users who have rated the opinion:
def agree_list
list = OpinionRating.find_all_by_opinion_id(params[:id])
#agree_list = []
list.each do |r|
user = Profile.find(r.profile_id)
#agree_list << user
end
end
Thank you
will_paginate 3.0 is designed to take advantage of the new ActiveRecord::Relation in Rails 3, so it defines paginate only on relations by default. It can still work with an array, but you have to tell rails to require that part.
In a file in your config/initializers (I used will_paginate_array_fix.rb), add this
require 'will_paginate/array'
Then you can use on arrays
my_array.paginate(:page => x, :per_page => y)
You could use Array#from to simulate pagination, but the real problem here is that you shouldn't be using Array at all.
This is what ActiveRecord Associations are made for. You should read that guide carefully, there is a lot of useful stuff you will need to know if you're developing Rails applications.
Let me show you a better way of doing the same thing:
class Profile < ActiveRecord::Base
has_many :opinion_ratings
has_many :opinions, :through => :opinion_ratings
end
class Opinion < ActiveRecord::Base
has_many :opinion_ratings
end
class OpinionRating < ActiveRecord::Base
belongs_to :opinion
belongs_to :profile
end
It's important that your database schema is following the proper naming conventions or all this will break. Make sure you're creating your tables with Database Migrations instead of doing it by hand.
These associations will create helpers on your models to make searching much easier. Instead of iterating a list of OpinionRatings and collecting the users manually, you can make Rails do this for you with the use of named_scope or scope depending on whether you're using Rails 2.3 or 3.0. Since you didn't specify, I'll give both examples. Add this to your OpinionRating class:
2.3
named_scope :for, lambda {|id|
{
:joins => :opinion,
:conditions => {
:opinion => { :id => id }
}
}
}
named_scope :agreed, :conditions => { :agree => true }
named_scope :with_profiles, :includes => :profile
3.0
scope :agreed, where(:agree => true)
def self.for(id)
joins(:opinion).where(:opinion => { :id => id })
end
In either case you can call for(id) on the OpinionRatings model and pass it an id:
2.3
#ratings = OpinionRating.agreed.for(params[:id]).with_profiles
#profiles = #ratings.collect(&:profile)
3.0
#ratings = OpinionRating.agreed.for(params[:id]).includes(:profile)
#profiles = #ratings.collect(&:profile)
The upshot of all this is that you can now easily paginate:
#ratings = #ratings.paginate(:page => params[:page])
Update for Rails 4.x: more or less the same:
scope :agreed, ->{ where agreed: true }
def self.for(id)
joins(:opinion).where(opinion: { id: id })
end
Although for newer Rails my preference is kaminari for pagination:
#ratings = #ratings.page(params[:page])
The gem will_paginate will paginate both ActiveRecord queries and arrays.
list = OpinionRating.where(:opinion_id => params[:id]).includes(:profile).paginate(:page => params[:page])
#agree_list = list.map(&:profile)
If you don't want to use the config file or are having trouble with it, you can also just ensure you return an ActiveRecord::Relation instead of an array. For instance, change the agree_list to be a list of user ids instead, then do an IN on those ids to return a Relation.
def agree_list
list = OpinionRating.find_all_by_opinion_id(params[:id])
#agree_id_list = []
list.each do |r|
user = Profile.find(r.profile_id)
#agree_id_list << user.id
end
#agree_list = User.where(:id => #agree_id_list)
end
This is inefficient from a database perspective, but it's an option for anybody having issues with the will_paginate config file.
I took advantage of rails associations, and came up with a new method:
def agree_list
o = Opinion.find(params[:id])
#agree_list = o.opinion_ratings(:conditions => {:agree => true}, :order => 'created_at DESC').paginate :page => params[:page]
rescue ActiveRecord::RecordNotFound
redirect_to(profile_opinion_path(session[:user]))
end
In my view I looked up the profile like so:
<% #agree_list.each do |rating| %>
<% user = Profile.find(rating.profile_id) %>
<% end %>
Please post up if there's a better way to do this. I tried to use the named_scope helper in the OpinionRating model with no luck. Here's an example of what I tried, but doesn't work:
named_scope :with_profile, lambda {|id| { :joins => [:profile], :conditions => ['profile_id = ?', id] } }
That seemed like the same as using the find method though.
Thanks for all the help.
I am using rails 3 ruby 1.9.2. Also, I am just starting app, so no css or styles included.
Install will_paginate:
gem install will_paginate
Add to Gemfile and run bundle.
Controller
class DashboardController < ApplicationController
include StructHelper
def show
#myData =structHelperGet.paginate(:page => params[:page])
end
end
module StructHelper queries a service, not a database.
structHelperGet() returns an array of records.
Not sure if a more sophisticated solution would be to fake a model, or to grab the data every so often and recreate a sqllite table once in a while and have a real model to query. Just creating my first rails app ever.
View
<div id="Data">
<%= will_paginate #myData%>
<table>
<thead>
<tr>
<th>col 1</th>
<th>Col 2</th>
<th>Col 3</th>
<th>Col 4</th>
</tr>
</thead>
</tbody>
<% #myData.each do |app| %>
<tr>
<td><%=app[:col1]%> </td>
<td><%=app[:col2]%> </td>
<td><%=app[:col3]%> </td>
<td><%=app[:col4]%> </td>
</tr>
<% end %>
</tbody>
</table>
<%= will_paginate #myData%>
</div>
This will give you pagnation of the default 30 rows per page.
If you have not read http://railstutorial.org yet, start reading it now.
You can implement pagination even without any gem.I saw this How do I paginate an Array?. Simple implementation in kaminari gems doc. Please see the below example which i got from kaminari gems doc
arr = (1..100).to_a
page, per_page = 1, 10
arr[((page - 1) * per_page)...(page * per_page)] #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
page, per_page = 2, 10
arr[((page - 1) * per_page)...(page * per_page)] #=> [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Here's some of my production code (I had to force line breaks):
task = Task.find_or_create_by_username_and_timestamp_and_des \
cription_and_driver_spec_and_driver_spec_origin(username,tim \
estamp,description,driver_spec,driver_spec_origin)
Yes, I'm trying to find or create a unique ActiveRecord::Base object. But in current form it's very ugly. Instead, I'd like to use something like this:
task = Task.SOME_METHOD :username => username, :timestamp => timestamp ...
I know about find_by_something key=>value, but it's not an option here. I need all values to be unique. Is there a method that'll do the same as find_or_create_by, but take a hash as an input? Or something else with similat semantics?
Rails 3.2 first introduced first_or_create to ActiveRecord. Not only does it have the requested functionality, but it also fits in the rest of the ActiveRecord relations:
Task.where(attributes).first_or_create
In Rails 3.0 and 3.1:
Task.where(attributes).first || Task.create(attributes)
In Rails 2.1 - 2.3:
Task.first(:conditions => attributes) || Task.create(attributes)
In the older versions, you could always write a method called find_or_create to encapsulate this if you'd like. Definitely done it myself in the past:
class Task
def self.find_or_create(attributes)
# add one of the implementations above
end
end
I also extend the #wuputah's method to take in an array of hashes, which is very useful when used inside db/seeds.rb
class ActiveRecord::Base
def self.find_or_create(attributes)
if attributes.is_a?(Array)
attributes.each do |attr|
self.find_or_create(attr)
end
else
self.first(:conditions => attributes) || self.create(attributes)
end
end
end
# Example
Country.find_or_create({:name => 'Aland Islands', :iso_code => 'AX'})
# take array of hashes
Country.find_or_create([
{:name => 'Aland Islands', :iso_code => 'AX'},
{:name => 'Albania', :iso_code => 'AL'},
{:name => 'Algeria', :iso_code => 'DZ'}
])
Is there any way of overriding a model's id value on create? Something like:
Post.create(:id => 10, :title => 'Test')
would be ideal, but obviously won't work.
id is just attr_protected, which is why you can't use mass-assignment to set it. However, when setting it manually, it just works:
o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888
I'm not sure what the original motivation was, but I do this when converting ActiveHash models to ActiveRecord. ActiveHash allows you to use the same belongs_to semantics in ActiveRecord, but instead of having a migration and creating a table, and incurring the overhead of the database on every call, you just store your data in yml files. The foreign keys in the database reference the in-memory ids in the yml.
ActiveHash is great for picklists and small tables that change infrequently and only change by developers. So when going from ActiveHash to ActiveRecord, it's easiest to just keep all of the foreign key references the same.
You could also use something like this:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Although as stated in the docs, this will bypass mass-assignment security.
Try
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
that should give you what you're looking for.
For Rails 4:
Post.create(:title => 'Test').update_column(:id, 10)
Other Rails 4 answers did not work for me. Many of them appeared to change when checking using the Rails Console, but when I checked the values in MySQL database, they remained unchanged. Other answers only worked sometimes.
For MySQL at least, assigning an id below the auto increment id number does not work unless you use update_column. For example,
p = Post.create(:title => 'Test')
p.id
=> 20 # 20 was the id the auto increment gave it
p2 = Post.create(:id => 40, :title => 'Test')
p2.id
=> 40 # 40 > the next auto increment id (21) so allow it
p3 = Post.create(:id => 10, :title => 'Test')
p3.id
=> 10 # Go check your database, it may say 41.
# Assigning an id to a number below the next auto generated id will not update the db
If you change create to use new + save you will still have this problem. Manually changing the id like p.id = 10 also produces this problem.
In general, I would use update_column to change the id even though it costs an extra database query because it will work all the time. This is an error that might not show up in your development environment, but can quietly corrupt your production database all the while saying it is working.
we can override attributes_protected_by_default
class Example < ActiveRecord::Base
def self.attributes_protected_by_default
# default is ["id", "type"]
["type"]
end
end
e = Example.new(:id => 10000)
Actually, it turns out that doing the following works:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
As Jeff points out, id behaves as if is attr_protected. To prevent that, you need to override the list of default protected attributes. Be careful doing this anywhere that attribute information can come from the outside. The id field is default protected for a reason.
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(Tested with ActiveRecord 2.3.5)
Post.create!(:title => "Test") { |t| t.id = 10 }
This doesn't strike me as the sort of thing that you would normally want to do, but it works quite well if you need to populate a table with a fixed set of ids (for example when creating defaults using a rake task) and you want to override auto-incrementing (so that each time you run the task the table is populate with the same ids):
post_types.each_with_index do |post_type|
PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end
Put this create_with_id function at the top of your seeds.rb and then use it to do your object creation where explicit ids are desired.
def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
obj
end
and use it like this
create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})
instead of using
Foo.create({id:1,name:"My Foo",prop:"My other property"})
This case is a similar issue that was necessary overwrite the id with a kind of custom date :
# in app/models/calendar_block_group.rb
class CalendarBlockGroup < ActiveRecord::Base
...
before_validation :parse_id
def parse_id
self.id = self.date.strftime('%d%m%Y')
end
...
end
And then :
CalendarBlockGroup.create!(:date => Date.today)
# => #<CalendarBlockGroup id: 27072014, date: "2014-07-27", created_at: "2014-07-27 20:41:49", updated_at: "2014-07-27 20:41:49">
Callbacks works fine.
Good Luck!.
For Rails 3, the simplest way to do this is to use new with the without_protection refinement, and then save:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save
For seed data, it may make sense to bypass validation which you can do like this:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)
We've actually added a helper method to ActiveRecord::Base that is declared immediately prior to executing seed files:
class ActiveRecord::Base
def self.seed_create(attributes)
new(attributes, without_protection: true).save(validate: false)
end
end
And now:
Post.seed_create(:id => 10, :title => 'Test')
For Rails 4, you should be using StrongParams instead of protected attributes. If this is the case, you'll simply be able to assign and save without passing any flags to new:
Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}`
In Rails 4.2.1 with Postgresql 9.5.3, Post.create(:id => 10, :title => 'Test') works as long as there isn't a row with id = 10 already.
you can insert id by sql:
arr = record_line.strip.split(",")
sql = "insert into records(id, created_at, updated_at, count, type_id, cycle, date) values(#{arr[0]},#{arr[1]},#{arr[2]},#{arr[3]},#{arr[4]},#{arr[5]},#{arr[6]})"
ActiveRecord::Base.connection.execute sql