How to copy or clone model? - ruby-on-rails

I have a model Book with attributes id, name, price. I have an instance of Book:
b1 = Book.new
b1.name = "Blah"
b1.price = 12.5
b1.save
I would like to copy b1, create another instance of the Product model. I'm tryid p1=b1.clone then p1.save but it didn't work. Any idea?
And my environment is:
Netbeans 6.9 RC2
JRuby 1.5.0
EDITED:
My TemporaryProduct model:
class Admin::TemporaryProduct < ActiveRecord::Base
def self.update_from_web_service(web_service_url)
response = HTTParty.get(web_service_url)
response["webServiceResult"]["product"].each do|element|
unless exists? :orignal_product_id => element['id']
create!(
:name => element['name'],
:price => element['price'],
:amount => element['amount'],
:description => element['description'],
:orignal_product_id => element['id'],
:image => element['image'],
:shop_account_number => element['shopAccountNumber'],
:unit => element['unit']
)
end
end
end
end
Product is create action:
def create
#temporary_products = Admin::TemporaryProduct.find_all_by_orignal_product_id(params[:product])
#product = Admin::Product.new(#temporary_products.attributes)
# #product = #temporary_products.clone
respond_to do |format|
format.html { redirect_to(admin_products_url, :notice => 'Admin::Product was successfully created.') }
end
end
I want to clone all b1's attributes to p1 model.

I think you want:
b2 = Book.create(b1.attributes)
Edit:
Given your create action above, I think what you want to do is change the line which starts #product to
#temporary_products.each {|tp| Admin::Product.create(tp.attributes)}
That will create a new Product object for each TemporaryProduct object, using the same attributes as the TemporaryProduct. If that's not what you want, let me know.

You can make duplicate record using dup in rails For Example,
b1 = Book.create(name: "example", price: 120)
b1.save
duplicate_record = b1.dup
duplicate_record.save!
or you can create first new record and then make a duplicate
Hope this is useful for you.

If by didn't work you mean that there is no new record in the database then you probably want to set the id of p1 to null before you save. If the clone has the same id as the original then it would appear to represent the same object.

Related

Rich many-to-many assosiation. Only allow one record

I need to limit my rich many-to-many association table to only one association per unique par. (rails).
That means i want terminals (model 1) and subscriptions (model 2) to have one custom_price (rich join) not any more than that.
How is this done most elegantly?
So far i have done:
def new_custom_price
#terminal_id = params[:terminal_id]
#subscription_id = params[:subscription_id]
#custom_price = CustomPrice.find_or_initialize_by_terminal_id_and_subscription_id( #terminal_id, #subscription_id)
render action: 'custom_price'
end
def create_custom_price
#terminal_id = params[:terminal_id]
#subscription_id = params[:subscription_id]
#custom_price = CustomPrice.new(custom_price_params.merge( :terminal_id => #terminal_id, :subscription_id => #subscription_id))
respond_to do |format|
if #custom_price.save
format.js { render action: 'add_custom_price' }
else
format.js { }
end
end
end
Two things you can do to handle this: (This is assuming "price" is the unique part of the many to many relationships)
1) Validation in CustomPrice
validates :price, uniqueness: {scope: [:terminal_id, :subscription_id]}
Unique Price on CustomPrice with a scope of Terminal and Subscription IDs
http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates
2) Use helpers
CustomPrice.find_or_create_by(custom_price_params.merge( :terminal_id => #terminal_id, :subscription_id => #subscription_id))
Similar to the one used in your new action
http://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-find_or_create_by

How to find the ids used by current user

I have created a quiz and i displayed the items in one per page.. i want the values of question id that is visited by the current user . in this code i give next and it render the question i want the visited question id by current user
def next
#user = current_user
#student = Student.find_by_admission_no(#user.username)
#exam_group = ExamGroup.find_by_id(params[:exam_group_id])
#answer = Answer.new(params[:ans])
#answer.answer = params[:answer]
#answer.exam_group_id = #exam_group.id
#answer.user_id = #user.id
passed_question = params[:passed_question]
#answer.questions_id = passed_question
next_question = params[:next_question]
#question = Question.find_by_id(passed_question)
#module = Question.find_by_sql ["SELECT student_additional_field_id FROM questions WHERE id=#{passed_question}"]
student_additional_field_id = #module[0].student_additional_field_id
#questions = Question.find(:all, :conditions => [' exam_group_id=? && student_additional_field_id=? ',#exam_group,student_additional_field_id], :offset => #ans)
#ques = []
#questions.shuffle.each do |a|
#ques.push a.id unless a.id.nil?
end
a = #ques[0]
session[:ques_id] = a
#answer.modules_id = student_additional_field_id
if params[:answer] == #question.is_answer
#answer.marks = 1
else
#answer.marks = 0
end
if #answer.save
#ans = Question.find_by_id(a, :conditions => [' id not in (?) && exam_group_id=?',answered, #exam_group])
unless #ans.nil?
render(:update) do |page|
page.replace_html 'main', :partial => 'ans', :object => #ans
end
else
render(:update) do |page|
page.replace_html 'main', :partial => 'ans2'
end
end
end
end
It seems your problem is to do with Rails model associations:
Why do we need associations between models? Because they make common
operations simpler and easier in your code. For example, consider a
simple Rails application that includes a model for customers and a
model for orders. Each customer can have many orders
ActiveRecord Associations
ActiveRecord associations allow you to define a primary_key (typically an ID), which you can then reference on other tables through a foreign_key. The foreign key is typically a table column which will be called something like user_id, or another type of ID
You can associate your models using these functions:
belongs_to
has_one
has_many
has_many :through
has_one :through
has_and_belongs_to_many
To answer your question, you're obviously getting the current_user ID okay - so if you managed to get the relations set up correctly, you could literally call the data in this way:
#user = User.find(current_user.id)
#exam_group = #user.exams_groups
I would go through your code & put more info into it.... but I think you need to learn about ActiveRecord associations first

Rails Dry up Find or Create

I have the following method in my model which uses find_or_create_by to find or create a new product.
def self.save_prod(product)
Product.find_or_create_by_prod_id(product)
product_data = ItemData.get_product_data(product)
p.update_attributes(
:prod_id => product,
:upc => product_data[:upc],
:title => product_data[:title]
)
end
The ItemData.get_product_data() method is a module method which calls an API to fetch product data:
def self.get_product_data(product)
url_raw = URI.parse("http://www.api.com/v1/itemid=#{product}")
url = Net::HTTP.get_response(url_raw).body
#resp = JSON.parse(url)
#title = Sanitize.clean(#resp["serviceResult"]["itemName"]).strip
#upc = #resp["serviceResult"]["uPC"]
{:title => #title, :upc => #upc}
end
This works as expected, however I know it can be a LOT more efficient, by not calling the ItemData.get_product_data() method every time the save_prod() method is called. How can I add new product data without having to call the ItemData.get_product_data() if a product already exists.
Another way to doing it. This would return the Product object if it is already present otherwise it will create it from api and return the new object.
def self.save_prod(product)
Product.find_by_prod_id(product) || Product.create( ItemData.get_product_data(product) )
end
Modify the api call to return a hash with prod_id. Not sure why you are converting title and upc to class variables here. It could lead to problems if they are used extensively.
def self.get_product_data(product)
url_raw = URI.parse("http://www.api.com/v1/itemid=#{product}")
url = Net::HTTP.get_response(url_raw).body
#resp = JSON.parse(url)
#title = Sanitize.clean(#resp["serviceResult"]["itemName"]).strip
#upc = #resp["serviceResult"]["uPC"]
{:title => #title, :upc => #upc, :prod_id => product}
end
Instead of doing a find or create use find or initialize by . Change your code to following :
prod = find_or_initialize_by_prod_id(product)
if prod.new_record?
prod.save!
product_data = ItemData.get_product_data(product)
prod.update_attributes(
:prod_id => product,
:upc => product_data[:upc],
:title => product_data[:title]
)
end
by using find_or_initalize you can distinguish whether the record was created or found by using new_record method. If new you can save and make an API call and do whatever you want.

Rails checking db for already existing user and add comment without creating a new user

I have a landlord and comment class.
Landlord has 1:N comments.
When a landlord is created it creates a comment on the same form (nested).
When the form is submitted, users_controller#create is called.
I want to check the database if the landlord with the same name, city and state already exists and add the comment to that landlord instead of creating a new one.
def create
#check if a landlord of the same name already exists and add comments to that
if Landlord.find_by_name(params[:name]) && Landlord.find_by_city(params[:city])&& Landlord.find_by_province(params[:province])
#landlord_exists = Landlord.find_by_name(params[:name]) && Landlord.find_by_city(params[:city])&& Landlord.find_by_province(params[:province])
#landlord_exists.comments.build
#landlord_exists.comments[0].setIP request.remote_ip
#landlord_exists.save
else
#landlord = Landlord.new(params[:landlord])
#landlord.comments[0].setIP request.remote_ip
if #landlord.save
flash[:success] = "Thank you for submitting a Landlord"
redirect_to landlords_path
else
end
end
end
update # 1 down to
def create
#landlord = Landlord.where(:name => params[:name], :city => params[:city], :province => params[:province]).first_or_create!
#landlord.comments[0].setIP request.remote_ip
if #landlord.save
redirect_to landlords_path
else
end
end
The line
#landlord = Landlord.where(:name => params[:name], :city => params[:city], :province => params[:province]).first_or_create!
Seems to be returning a nil object thus throwing errors when setIP is called.
What would be causing this? I have tried it in the terminal and it worked fine although I was using hardcoded values.
The simplest way to do it would be to use ActiveRecord::Relation's first_or_create! method. This will perform a query based on the params entered in the form, and create a record if no matches are found, meaning you can get of the if ... else conditionals:
#landlord = Landlord.where(params[:landlord]).first_or_create!
#landlord.comments.build
# etc ...

ruby on rails can i use something like Model.find(params[:id]).where()

Hi, I am still a student and I'm taking a software engineering course and we have this big project (web design) and we're using rails so my question is I have a table Users and a table Groups and another association table GroupUsers where it has two foreign keys user_id and group_id. Whenever a user creates or join an already created group his id and the group_id are added to GroupUsers table
module GroupUsersHelper
def join
#group_id = params[:id]
#user_id = params[:user_id]
#newuser= GroupUser.new(:group_id => #group_id, :user_id => #user_id)
#newuser.save
redirect_to(:controller => 'groups', :action => 'show', :id => #group_id)
end
end
Now I have to create method leave group where I'll have to destroy the record from GroupUsers, so I wrote this code also in GroupUsersHelper
def leave
#group_id = params[:group_id]
#user_id = params[:user_id]
#group_user_id = params[:group_user_id]
#newuser= GroupUser.find(#group_user_id).where(:group_id => #group_id, :user_id =>
#user_id)
#newuser.destroy
redirect_to(:controller => 'groups', :action => 'show', :id => params[:id])
end
but I get this error
ActiveRecord::RecordNotFound in GroupsController#leave
Couldn't find GroupUser without an ID
If you need more info about the code please let me know.
The other answers addressed your code example, but not the question in the title. You cannot use Model.find(params[:id]).where() in ActiveRecord (Rails' default ORM). Model.find returns a single record (an instance of your Model class) but Model.where returns a Model::ActiveRecord_Relation object (an array-like object). The .where method is only available on your Model class directly (i.e. Model.where) or on an ActiveRecord_Relation. If you are chaining methods, you need to make sure each method returns an ActiveRecord_Relation. For example:
Model.where("name = ?", "max")
.order(created_at: :desc)
.reorder(created_at: :asc)
.includes(:sent_comments)
Model.find(params[:id]) will generate such kind of query
*Lets take an example : GroupUser.find(params[:id]) will generate sql equivalent*
select * from group_users where id = params[:id]
If you want to add where condition do something like this
GroupUser.where("# id ={params[:id]} and someColumn = #{somevariable}")
You are not passing the groupuser_id in the params hash. Instead of storing it in the #group_id and #user_id, just try this:
#newuser= GroupUser.find_by_group_id_and_user_id(params[:group_id],params[:user_id])
Can you additionally post your params dump so we can figure out how to make a good redirect since i hence that will be a problem also.
Just do:
group = Group.find(params[:group_id])
user = User.find(params[:user_id])
group.users.delete(user)
group.save
This will remove the user from the users association, and will automatically destroy the GroupUser record when the group is saved. See the collection.delete method.
#max pleaner is right, you should not use Model.find().where().
It is incorrect, and if you think about it, you will see that it is redundant. To find the association you wish to destroy, you either need params[:group_id] and params[:user_id], or just params[:group_user_id].
If you have passed the params[:group_user_id] to the leave method, you already have all the information needed to find the association:
def leave
#newuser= GroupUser.find(params[:group_user_id])
#newuser.destroy
redirect_to(:controller => 'groups', :action => 'show', :id => params[:group_id])
end
Or alternatively, If you have passed the params[:group_id] and params[:user_id] to the leave method:
def leave
#newuser= GroupUser.find_by_group_id_and_user_id(params[:group_id], params[:user_id])
#newuser.destroy
redirect_to(:controller => 'groups', :action => 'show', :id => params[:group_id])
end
Also, there are a few other redundancies in your code that you will notice I omitted above. There is no need to assign each parameter to an instance variable before passing the parameter to the find() method:
#model_id = params[:model_id]
#model= Model.find(#model_id)
#can be simplified to:
#model = Model.find(params[:model_id])
Generally, you only want to create instances variables for a record or collection of records you will need to access in a view (such as a Group in the context of your application).

Resources