AcitveRecord where method that matches just one record - ruby-on-rails

Program.where(name: "xxyyzz123") will return a collection, even if there's just one record that matches which forces me to do ugly things like:
puts Program.where(name: "xxyyzz123").first.age
or
puts Program.where(name: "xxyyzz123")[0].age
When I know for sure only one record will match, is there a shorter way to grab a property from that one record?

ActiveRecord's dynamic attribute-based finders (find_by_x) allow you to select the first record that matches in your database. For example:
Program.find_by_name('xxyyzz123')
will return the first record with name = 'xxyyzz123'
Note that these finders are 'mildly deprecated' in Rails 4. Using
Program.find_by(name: 'xxyyzz123")
achieves the same thing and may make it easier when needing to update to the next version of Rails if they ever remove the former's functionality.
See ActiveRecord::Base in the API for more.

Yes, you will have to access that with Program.where(name: "xxyyzz123").first.age, however, in Rails 3, it is usually recommended to do that type of query with: Program.find_by_name('xxyyzz123').age.
Rails 4 deprecates the above syntax and recommends you to use the following syntax for that:
Program.find_by(name: 'xxyyzz123')
If you have multiple conditions, then simply : Program.find_by(name: 'xxyyzz123', lang: 'ruby')
Behind the scene, it does the same tomfoolery - where clause and returns first object.

Related

How to add attribute/property to each record/object in an array? Rails

I'm not sure if this is just a lacking of the Rails language, or if I am searching all the wrong things here on Stack Overflow, but I cannot find out how to add an attribute to each record in an array.
Here is an example of what I'm trying to do:
#news_stories.each do |individual_news_story|
#user_for_record = User.where(:id => individual_news_story[:user_id]).pluck('name', 'profile_image_url');
individual_news_story.attributes(:author_name) = #user_for_record[0][0]
individual_news_story.attributes(:author_avatar) = #user_for_record[0][1]
end
Any ideas?
If the NewsStory model (or whatever its name is) has a belongs_to relationship to User, then you don't have to do any of this. You can access the attributes of the associated User directly:
#news_stories.each do |news_story|
news_story.user.name # gives you the name of the associated user
news_story.user.profile_image_url # same for the avatar
end
To avoid an N+1 query, you can preload the associated user record for every news story at once by using includes in the NewsStory query:
NewsStory.includes(:user)... # rest of the query
If you do this, you won't need the #user_for_record query — Rails will do the heavy lifting for you, and you could even see a performance improvement, thanks to not issuing a separate pluck query for every single news story in the collection.
If you need to have those extra attributes there regardless:
You can select them as extra attributes in your NewsStory query:
NewsStory.
includes(:user).
joins(:user).
select([
NewsStory.arel_table[Arel.star],
User.arel_table[:name].as("author_name"),
User.arel_table[:profile_image_url].as("author_avatar"),
]).
where(...) # rest of the query
It looks like you're trying to cache the name and avatar of the user on the NewsStory model, in which case, what you want is this:
#news_stories.each do |individual_news_story|
user_for_record = User.find(individual_news_story.user_id)
individual_news_story.author_name = user_for_record.name
individual_news_story.author_avatar = user_for_record.profile_image_url
end
A couple of notes.
I've used find instead of where. find returns a single record identified by it's primary key (id); where returns an array of records. There are definitely more efficient ways to do this -- eager-loading, for one -- but since you're just starting out, I think it's more important to learn the basics before you dig into the advanced stuff to make things more performant.
I've gotten rid of the pluck call, because here again, you're just learning and pluck is a performance optimization useful when you're working with large amounts of data, and if that's what you're doing then activerecord has a batch api you should look into.
I've changed #user_for_record to user_for_record. The # denote instance variables in ruby. Instance variables are shared and accessible from any instance method in an instance of a class. In this case, all you need is a local variable.

Remove element from ActiveRecord_Relation in rails

How can i remove the last element from an ActiveRecord_Relation in rails?
e.g. if I set:
#drivers = Driver.all
I can add a another Driver object called #new_driver to #drivers by doing:
#drivers << #new_driver
But how can I remove an object from #drivers?
The delete method doesn't seem to work, i.e.
#drivers.delete(0)
You can use the reject! method, this will remove the object from the collection without affecting the db
for example:
driver_to_delete = #driver.first # you need the object that you want removed
#drivers.reject!{|driver| driver == driver_to_delete}
Very late too, but I arrived here looking for a fast answer and finished by thinking by myself ;)
Just to clarify about the different answers and the Rails 6.1 comment on accepted answer:
The OP wanted to remove one entry from a query, but NOT remove it from database, so any answer with delete or destroy is just wrong (this WILL delete data from your database !!).
In Ruby (and therefore Rails) convention, shebang methods (ending with !) tend to alter the given parameter. So reject! would imply modifying the source list ... but an ActiveRecord_Relation is basically just a query, NOT an array of entries !
So you'd have 2 options:
Write your query differently to specifically say you don't want some id:
#drivers.where.not(id: #driver_to_remove) # This still is an ActiveRecord_Relation
Use reject (NO shebang) on your query to transform it into an Array and "manually" remove the entry you don't want:
#drivers.reject{ |driver| driver == #driver_to_remove}
# The `reject` forces the execution of the query in DB and returns an Array)
On a performance point of view, I would personally recommend the first solution as it would be just a little more complex against the DB where the latter implies looping on the whole (eventually large) array.
Late to the question, but just had the same issue and hope this helps someone else.
reject!did not work for ActiveRecord_Relation in Rails 4.2
drop(1) was the solution
In this case #drivers.drop(0) would work to drop the first element of the relation
Since its an array of objects, have you tried to write something like #drivers.delete(#new_driver) or #drivers.delete(id: #new_driver.id) ?
This is the documentation you need:
#group.avatars << Avatar.new
#group.avatars.delete(#group.avatars.last)
--
.destroy
The problem you've got is you're trying to use collection methods on a non-collection object. You'll need to use the .destroy ActiveRecord method to get rid of the record from the database (and consequently the collection):
#drivers = Driver.all
#drivers.last.destroy
--
Scope
.delete will remove the record from the DB
If you want to pull specific elements from the db to populate the #drivers object, you'll need to use a scope:
#app/models/driver.rb
Class Driver < ActiveRecord::Base
scope :your_scope, -> { where column: "value" }
end
This will allow you to call:
#app/controllers/drivers_controller.rb
def index
#drivers = Driver.your_scope
end
I think you're getting the MVC programming pattern confused - data manipulation is meant to happen in the model, not the controller
As stated above, reject! doesn't work in Rails 4.2, but delete does, so #drivers.delete(#new_driver) works, and more generally:
#drivers.delete(Driver.where(your condition))

Rails4: Get an ActiveRecord::Relation from a model or an already chained relation

I'm writing a module for ActiveRecord models. In short it's a method that can call a series of where, join and order statements. The statements are not known at the time of writing so it is not possible to use scopes. So far it works well but there is one point I'd like to improve.
Here is my method:
def filter
rel = respond_to?(:filter_scope) ? filter_scope : where(1)
# Do other stuffs with `rel`
# ...
rel
end
It first call filter_scope if it is defined, or it obtain an ActiveRecord::Relation from the target model. To do so I use where(1) to force the model to return a relation object. This works well whenever I call filter directly on the model (User.filter) or on a relation (User.order(:name).filter, User.my_scope.filter.order(:age) etc...)
But using where(1) fells a bit dirty. In Rails 3 I would be using all instead but it's depreciated in Rails 4. Any idea on how to improve this?
Thanks in advance
Note: I cannot substitute where(1) by self because there is a possibility that self would be returned from filter and User.filter would be a class, therefore not usable as a query object.
In Rails 3 I would be using all instead but it's depreciated in Rails 4.
I don't think all is depreciated, it used to return an Array (rails 3) and now returns an ActiveRecord::Relation so you should be able to use it for chaining queries.
see http://api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/ClassMethods.html#method-i-all
Returns an ActiveRecord::Relation scope object.

Using non-numerical identifier in Rails find function

In many Rails examples I see
ModelName.find(:id) being used, supposed every model has a unique string attribute named :symbol, how can I set up the model so that find(:symbol) would work? Do I have to implement the searching algo myself?
You could use
ModelName.find_by_symbol("uniquestring")
More info on Rails Dynamic Finders
You might want to try "find_by_yoursymbol"
ModelName.find_by_yoursymbol("symbol value")
I just used this to check if a record exists and to create it if it isn't there.
Spree::MailMethod.create(:environment => "test") unless Spree::MailMethod.find_by_environment("test").present?

Check if record exists in Rails before creating

I am trying to search my database before I enter the record, by doing this:
Product.update_or_create_by_name_and_date_and_applicationURL_and_server_and_addi_servers(app_name, app_date,url_app,server_name,addi_servers)
the problem is that I get an undefined method exception!
Is there another way to search for the same record before entering one?
You should use two steps:
#Suggestion 1
obj = Product.find_or_create_by_...
#Suggestion 2
obj = Product.find_or_initialize_by_...
obj.update_attributes hash_here
Rereading, your question, I can't really understand what do you want to update if you try to find an object with known attributes. Anyway, you would just have to adapt my answer a little if some fields are for identifying and some for update.
I would define a function in your model: something like
Product.find_by_everything
where you write out all the parameters of the search, instead of using the the long naming method.
Then, if that returns nil, create the product. This doesn't seem to be a good use case of using the built in activerecord naming methods.

Resources