Using Rails 4 with GraphQL API.
I'm getting some inputs via an object, based on which I'm finding or initializing new ActiveRecord objects that I want to save later.
Sample input is:
[
{:id=>"192", :internalId=>128, :title=>"Editing"},
{:internalId=>130, :title=>"New"}
]
As you can notice, some of the records already exist and have an ID, we need to update those. And the rest we need to save as new records.
Then I have a method that goes through those post values:
def posts=(value)
#posts = value.map do |post|
init_post(post)
end
end
def init_post(post)
Post.find_or_initialize_by(
id: post[:id],
title: post[:title],
internal_id: post[:internalId],
)
end
That will return two instances of the Post model:
[#<Post id: 192, title: "Editing", internal_id: 128, created_at: nil, updated_at: nil>, #<Post id: nil, title: "New", internal_id: 130, created_at: nil, updated_at: nil>]
Finally, I want to save both records:
def save_posts
posts.each(&:save)
end
Which will return:
"#<ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry '192' for key 'PRIMARY': INSERT INTO `posts` ..."
So how do I make sure the instances with ID just update the existing record, and the rest just save as new ones?
You can find, change/create and save it at once
Post.find_or_initialize_by(id: post[:id]).tap do |record|
record.title = post[:title]
record.internal_id = post[:internalId]
record.save
end
Related
I'm using Rails 5 and minitest. In minitest, how do I lookup an object from the database by one of its attributes (e.g. not the fixtures designation for the object). In my test I have
item = items(:one)
,,, do some db manipulation to this object ...
Item.all.each do |i|
puts "#{i.inspect}"
end
updated_item = Item.find(id: item.id)
But the line
updated_Item = Item.find(id: item.id)
dies with the error
ActiveRecord::RecordNotFound: Couldn't find Item with 'id'={:id=>980190962}
What is odd is taht in the lines above, where I print out the records in my database, I can see the object in question with the ID that Rails claims not to find ...
#<Item id: 298486374, name: "MyString", rating: 0, score: 0, created_at: "2018-01-19 20:25:05", updated_at: "2018-01-19 20:25:05">
#<Item id: 980190962, name: "Item1", rating: 1, score: 5, created_at: "2018-01-19 20:25:05", updated_at: "2018-01-19 20:25:05">
What am I doing wrong? How do I lookup the updated object?
find() looks by id , no need to tell, so:
updated_Item = Item.find(item.id)
Like the title said, I'm trying to get the ID of the record I just created, I tried to get it in the console but the ID is nil.
Here is the print I got of self in the console at the beginning of after_create
<Unit id: nil, uuid: "9f11be13-8d07-4471-a3bb-f87943966bbd", organization_id: 33, property_id: 430, door_number: "3", created_at: "2014-12-05 13:27:57", updated_at: "2014-12-05 13:27:57", deleted_at: nil, size: "5 1/2", door_number_int: 3, leases_count: 0, tasks_count: 0, notes: nil, state_id: 68, state_tasks_count: 2, current_lease_id: nil, next_lease_id: nil, state_tasks_serie: 1, state_tasks_serie_count: 2, price_asked: #<BigDecimal:7fc79162cb80,'0.123E3',9(18)>, availability_date: nil, comments_count: 0>
Is there a way to get access to the record ID?
This is what I tried so far
after_create
self.save
end
before_save
if self.id.present?
# do stuff
end
end
It is not very pretty but it work
To answer #MarekLipka : It doesn't cause an infinite loop because the record is created only once.
I also tried to use :
after_create
reload
# do stuff if self.id
end
but that doesn't seem to work.
Like #PauloFidalgo said, I can't get the id except in the after_save method. Although it is strange to me that I can get it in the second before_save (triggered by the save in after_create).
The id is only assigned on save, on create you are just creating the skeleton object and giving some values.
To get the id of the object just call object.id
If for some reason you want to get the id in the after_* methods, you need to use the after_save:
after_save {id = self.id}
You could also allocate an ID in create, but you need to query the database and set the value in the variable. To achieve this you need to use a sequence in database to set the id's.
I'm creating an active record object, and I want to test that I'm doing it correctly.
Here's my test code:
describe '#transaction process' do
let(:penalty_purchase) { penalty.penalty_purchase }
describe '#transaction' do
it 'a transaction between the seller and the admin, includes the penalty purchase and the sellers sale' do
penalty.purchase= penalty_purchase
args = {sale: sale, purchase: penalty_purchase}
wanted = penalty.transaction_factory.new(args).transaction
expect(penalty.transaction).to eql wanted
end
end
end
Heres the code my test is testing:
def penalty_transaction
args = {sale: sale, purchase: purchase}
#transaction = transaction_factory.new(args).transaction
end
The transaction factory object above takes in 2 other active record objects, a sale and a purchase.
For some reason the test is failing with this:
1) LockedSalePenalty purchase process #transaction process #transaction a transaction between the seller and the admin, includes the penalty purchase and the sellers sale
Failure/Error: expect(penalty.transaction).to eql wanted
expected: #<Transaction id: nil, purchase_id: nil, sale_id: 1, created_at: "2014-08-03 00:23:45", updated_at: nil, seller_id: 1, buyer_id: 2, amount: 20.0, reduced_asset_id: nil>
got: #<Transaction id: nil, purchase_id: nil, sale_id: 1, created_at: "2014-08-03 00:23:45", updated_at: nil, seller_id: 1, buyer_id: 2, amount: 20.0, reduced_asset_id: nil>
(compared using eql?)
I know that for objects you create you need implement your own equality methods for objects you create, so in my active record transaction class:
def eql?(obj)
self.class == obj.class &&
self.attributes == obj.attributes
end
When I compare the attributes of penalty.transaction and wanted, they are not equal, even though they look the exact same. Any ideas?
Edit:
The problem was that the created at attribute was different by a small amount.
I have this request to database
#show = Question.where(:question_status => params[:id])
Then in #show variable I have this: [#<Question id: 38, user_id: 1, question: "hi", question_status: 1, created_at: "2013-06-04 18:32:28", updated_at: "2013-06-04 18:32:28">, #<Question id: 40, user_id: 1, question: "urll", question_status: 1, created_at: "2013-06-04 18:34:57", updated_at: "2013-06-04 18:34:57">, #<Question id: 41, user_id: 1, question: "urll", question_status: 1, created_at: "2013-06-04 18:35:31", updated_at: "2013-06-04 18:35:31">]
How get , for example, question field ?
I trying #show.question but have error no defined method question.
#show = Question.find_by_question_status(params[:id])
and #show.question
If you us where statement then use:
#show.each do |show|
show.question
end
#show is an ActiveRecord::Relation object(Array). It does not belong to Question class.
#show = Question.where(:question_status => params([:id]).first
if you are expecting only one result. Otherwise you have to iterate the array for each element
You want to take an array of all question fields?
questions = Question.where(:question_status => params[:id]).map(&:question)
Then you can simply go like
questions.each{|question| puts question }
Try this code at your rails console
#show = Question.where(:question_status => params[:id])
#show.each_with_index do |ques, index|
p "#{index+1} :: #{ques.question}"
end
With the help of index, you will get the sequence number of rows.
Use #show.map(&:question) . This will give you all the questions in array.
#show.question gives you error no defined method question because you are trying to operate question on array #show.
If you do #show.first.question it will give you question of first object.
And
If you do #show.map(&:question) it will give you array of question.
To get all the information.
#show.each do |show|
puts show.question
puts show.question_status
puts show.user_id
end
You can optimize it as per you requirement.
Let's say I have this simple method in my helper that helps me to retrieve a client:
def current_client
#current_client ||= Client.where(:name => 'my_client_name').first
end
Now calling current_client returns this:
#<Client _id: 5062f7b851dbb2394a00000a, _type: nil, name: "my_client_name">
Perfect. The client has a few associated users, let's look at the last one:
> current_client.user.last
#<User _id: 5062f7f251dbb2394a00000e, _type: nil, name: "user_name">
Later in a new method I call this:
#new_user = current_client.user.build
And now, to my surprise, calling current_client.user.last returns
#<User _id: 50635e8751dbb2127c000001, _type: nil, name: nil>
but users count doesn't change. In other words - it doesn't add the new user but one user is missing... Why is this? How can I repair it?
current_client.users.count makes a round trip to the database to figure out how many user records are associated. Since the new user hasn't been saved yet (it's only been built) the database doesn't know about it.
current_client.users.length will give you the count using Ruby.
current_client.users.count # => 2
current_client.users.length # => 2
current_client.users.build
current_client.users.count # => 2
current_client.users.length # => 3