Current Code
#current_site_name = 'SITENAME'
#current_site = Site.where(:name => #current_site_name)
#current_site_id = #current_site.id
Basically, the name column in our Site model is unique. We are selecting a row using the site name as a parameter.
I am returned: <ActiveRecord::Relation:0x59426bc1>
I understand that I cannot simply ask for #current_site.id but is there any way to put the ID of the single activerecord into the variable #current_site_id
Again, there will always be one active record.
As you've seen, where will return an ActiveRecord::Relation (a set of rows), even if there's only one row that matches your criteria. You can make #current_site be assigned to the row you want by changing the line to this:
#current_site = Site.where(:name => #current_site_name).first
Hmm... This is quite strange.
Site.where(:name => #current_site_name).methods.include? :id #=> false
but
Site.where(:name => #current_site_name).instance_methods.include? :id #=> true
However,
Site.where(:name => #current_site_name).instance_eval "id" #=> NameError
and
Site.where(:name => #current_site_name).methods.include? "self.id" #=> NoMethodError
That said, a workaround assuming in your case, only one record should be returned is
Site.where(:name => #current_site_name).instance_eval('find_first').id
The find_first method is protected so we have to jump scope in its execution to avoid another NoMethodError
Just to throw another solution out there, you can make use of Rails' dynamic finders that are created for your models.
#current_site = Site.find_by_name(#current_site_name)
This returns a single record (and thus doesn't require the use of first) and might be seen as cleaner code in some people's opinion.
Related
I think the best way for me to explain this question is with example. Here is a simple method with a hash:
def getValues(table)
allColumns = {
'User' => ['first_name', 'last_name'],
'Vehicle' => ['make', 'model', 'id'],
}
end
I am trying to pass in the table and based on that table return a range of values. I would like to know what would be (performance-wise) the best way to accomplish this. Is it using a switch statement, if/else, some sort of loop? If you come up with an answer, please be as kind to include an example so that I may understand better.
I suggest you to rename the parameter first, maybe to table_name or something more descriptive.
Second it is kind of a convention in ruby to use method names separated by _, and avoid using camelCase as another languages.
Third, i would put the list on a constant variable or something, and just reference it inside the method.
Then you can look up the values of some hash based on a key just by using hash[key].
LIST = {
'User' => ['first_name', 'last_name'],
'Vehicle' => ['make', 'model', 'id'],
}
def get_values(table_name)
LIST[table_name]
end
Hash lookup by key is probably one of the most performant operations you could do with a collection, so there is no need to worry about it.
Foo.where(:some_id => 1).update_all(:some_columnn => "1")
Is this the right way to update Foo? I don't want to do a find and update the object.
As of Rails 4, the conditions are no longer supplied on the update_all method, but are instead specified on the preceding collection. For example,
# updates everything, as usual
Foo.update_all(some_column: '1')
# update only the specified rows
Foo.where(some_id: 1).update_all(some_column: '1')
Yes it is the right way, but remember, no callbacks or validations will be executed.
BTW, update_all accepts conditions also. Like this
Foo.update_all({:some_columnn => "1"}, {:some_id => 1})
It is the right approach if you don't want to instantiate an object, but keep in mind that this also means it won't perform any of your models validations or callbacks - it goes straight to a SQL update command.
Further information
You can use conditions,according to the api of update_all
update_all(updates, conditions = nil, options = {})
So you can do:
Foo.update_all(:some_column => '1', :some_id => 1)
The answer on this question has provided me with a nice roadmap for how to generate select tags with data from a collection on an association.
This works nicely and everything is going great.
The issue I have now is, how do I handle an empty collection?
With the regular :type => :input, I can just specify :nil => "Some nil message here".
But that doesn't seem to work for the collection, and to make matters worse, when there is nothing in the collection it seems to be displaying some integers (i.e. 1 and 2). I am assuming those are the IDs from the previously displayed objects in the collection, but for obvious reasons that doesn't make much sense.
Any ideas on how I can handle an empty collection with this gem?
Thanks.
Edit 1:
One alternative is to just put my original best_in_place helper tag inside an if statement for when a collection is not nil. But then how does the user edit it when it is blank? Perhaps there may be no way to handle this, because it would involve creating a new record in the collection.
I use a "workaround" for the empty options in a select tag, it could help you:
:type => :select, :collection => #my_colletion || [[I18n.t('common.none'), -1]]
When #my_colletion is nil, it shows a choice named 'None' with id = -1 (wich is not that bad to handle in the backend).
This part of code assumes the #my_collection is an array of arrays like [ [key, value], [key, value], ... ] OR nil.
Although, if you want your MyModel.all collection to fit the conditions for best_in_place, you can use the following:
#my_collection = MyModel.all.map{ |object| [object.name, object.value] }
# => this returns an array like [ [key, value], [key, value], ... ]
# => returns an empty array (NOT nil) if there is no entry in the DB
About the -1 id:
Using the -1 id as 'none' is easy because you don't need to explicitly handle the value nil (tests, etc). With the -1 id, you can use the following:
MyModel.where(id: params[:id]).first # => Returns the first object that has the params[:id]
# if params[:id] is -1, it will return nil and not raise an error.
I hope it helped :)
In my view page, i am using form_tag to create a form which will pass a string of ids from a hidden field to the controller code.
In my controller code, i am looping through an array of ids to update each record containing that id in the Expression table. But the code below does not seem to work.
I would really appreciate it if somebody could give me some suggestion regarding what is wrong with the code below.
def update_expression
#emi_ids_array = params[:emi_ids].split(/,/)
#sub_id = params[:sub_id]
#emi_ids_array.each do |emi_id|
#existing_exp = Expression.find(:first, :conditions => [ "EXT_EMI_ID = ? and EXT_SUB_FK = ?", emi_id, #sub_id])
#expression = #existing_exp.update_attributes(
:EXT_SUB_FK => #sub_id,
:EXT_PRESENCE => "present",
:EXT_STRENGTH => "weak",
:EXT_EMI_ID => emi_id
)
end
end
Try converting the array of ID's (and the sub_id) to integers.
Is it the finding of the object that fails, or the update? Output the #expression.errors after the update call to see if there are any validations failing.
Is there a reason for all the instance variables? You don't need the #'s if the variable doesn't go beyond that method. Also the #expression item seems superfluous, you're just duplicating the #existing_exp object, you don't need to put the return into a new object, especially if it's replaced each time the loop runs anyway.
Found a temporary solution. 'update_attributes' does not seem to work, so i opted for 'update_all' attribute
Expression.update_all({:EXT_PRESENCE => "present", :EXT_STRENGTH => "weak"},['EXT_EMI_ID = ? and EXT_SUB_FK = ?', emi_id, #sub_id])
Hopefully, it might be useful to someone else
I am trying to populate a nested field, product_name, it an Item.
In my Item model:
class Item < ActiveRecord::Base
attr_writer :product_name
belongs_to :order
belongs_to :product
def product_name
#Product.find_by_id(self.product_id) #=> returns the product object
#self.product #=> returns the product object
#Product.find_by_id(self.product_id).name #=> complains that 'name' is being called on nil
#self.product.name #=> complains that 'name' is being called on nil
#"foooobar" #=> returns "foooobar"
end
end
Each of those commented lines behaves as described.
What I don't get is, how can something return an object successfully, but then complain that the object is nil when you access an attribute of it?
Here is the whole error:
NoMethodError in Orders#edit
Showing app/views/orders/_form.haml where line #18 raised:
undefined method `name' for nil:NilClass
Extracted source (around line #18):
17: = item.hidden_field :product_id
18: = item.text_field :product_name, :class => 'auto_complete_input'
19: = item.text_field :quantity, :size => 2
Thanks
You may want to take a look at the andand plugin at http://github.com/raganwald/andand.
Basically, what it does is handle the errors that may arise from trying to call a method on another that has the possibility of being nil.
Based solely on your example:
Product.find_by_id(self.product_id).name #=> complains that 'name' is being called on nil
An implementation of andand would look like:
Product.find(self.product_id).andand.name
And even if the first statement, Product.find(self.product_id), returns nil it would no longer fire the undefined method on nil error.
PS:
Again, based solely on your code, I can see that you're reproducing the functionality generated by using the :belongs_to association. Essentially, what it means is that you can use:
self.product
insead of:
Product.find_by_id(self.product_id)
It sounds like you're asking a really general question. If you're asking a totally different question than I'm hearing, ignore this. :)
Yes, Ruby is successfully returning an object, and the object is nil. In Ruby, a method can return any value, and nil is just another value that a method can return. Those statements are probably doing a query that isn't returning any results, so the method returns nil instead of the object you are expecting.
The longer answer is that Product.find_by_id(x) calls a generated find_by_ attribute method for the id field, which is the same as calling Product.find(:first, :conditions => ["id = ?", x]). If you look at the documentation for .find, you will notice that it says
Find first - This will return [blah blah black...] If no record can be matched, nil is returned.
Have you given proper relationship in your product model and item model, i think giving proper association can solve your error.