I have this code in Rails 4:
mmkBase = Mmk::Base.find_or_initialize_by(id: id)
mmkBase.update(name: name, city: city, country: country, address: address, region_ids: regionIds, latitude: latitude,
longitude: longitude)
The code is working ok, but now I would need to gather some statistics, like:
Number of bases.
Number of new bases.
Number of loaded (updated) bases.
Number of bases is not a problem. Number of new bases, I think I could check if the object mmkBase returned by find_or_initialize comes with more attributes than just the id, for example with a name.
However, I don´t know how can I check if the update operation is run.
Is there any way to know this? Is there a better way to achieve this? Maybe, I should use a more traditional way to do it, with a select first?
However, I don´t know how can I check if the update operation is run
You can simply use...
if mmkbase.update mkbase_params
...
else
...
This will invoke the update method, generating a response determined by whether it was successful or not (true = success; false = validation failure)
Number of new bases
As mentioned, the new_record? method is what you need here...
if mmkbase.new_record? #-> mmkbase hasn't been saved to db yet
I'd do this:
mmkBase.find_or_create_by(mmkbase_params) do |base|
#do something if new base
end
private
def mmkbase_params
params.require(:mmkbase).permit(:name, :city, :country, :address, :region_ids, :latitude, :longitude)
end
If you're having to pull this type of data by its primary key (find_or_initialize_by id:), you're in trouble... how do you know if the primary key will remain constant?
Of course, this is not always the case. Changing user details would require you to reference the user id - however that data is rigid. IE a user registered (with name and email), and you're now looking for that user (it can't be created on the fly).
I don't know your schema; anything which can be created like this should be referenced by its core data (name, city, etc)...
mmkbase.find_by(name: ....)
In relational databases, you need to be dealing with the data, not the primary keys. In other types of db, you can deal with indexes, but with this type of setup, it's much better to work with the pure data.
You want to distinguish between an already existing record and a newly created record. On a new record which was only initialized by find_or_initialize_by, new_record? returns true. So for example you could write:
mmkBase = Mmk::Base.find_or_initialize_by(id: id)
new_base = mmkBase.new_record?
if mmkBase.update(name: name, city: city, country: country, address: address, region_ids: regionIds, latitude: latitude,
longitude: longitude)
# update was successful
if new_base
new_bases += 1
else
updated_bases += 1
end
else
# error handling
end
Note that it is probably not a good idea to use find_or_initialize_by with the ID of the record. See Rich Peck's answer.
You could probably check the updated_at timestamp on the object to see if it has been updated recently but the Rails 4 update method doesn't tell you whether an object was updated or not.
http://apidock.com/rails/ActiveRecord/Base/update/class
Related
Let's say I have a User with attributes name and badge_number
For a JavaScript autocomplete field I want the user to be able to start typing the user's name and get a select list.
I'm using Materialize which offers the JS needed, I just need to provide it the data in this format:
data: { "Sarah Person": 13241, "Billiam Gregory": 54665, "Stephan Stevenston": 98332 }
This won't do:
User.select(:name, :badge_number) => { name: "Sarah Person", badge_number: 13241, ... }
And this feels repetitive, icky and redundant (and repetitive):
user_list = User.select(:name, :badge_number)
hsh = {}
user_list.each do |user|
hsh[user.name] = user.badge_number
end
hsh
...though it does give me my intended result, performance will suck over time.
Any better ways than this weird, slimy loop?
This will give the desired output
User.pluck(:name, :badge_number).to_h
Edit
Though above code is one liner, it still have loop internally. Offloading such loops to database may improve the performance when dealing with too many rows. But there is no database agnostic way to achieve this in active record. Follow this answer for achieving this in Postgres
If your RDBMS is Postgresql, you can use Postgresql function json_build_object for this specific case.
User.select("json_build_object(name, badge_number) as json_col")
.map(&:json_col)
The whole json can be build using Postgresql supplied functions too.
User.select("array_to_json(array_agg(json_build_object(name, badge_number))) as json_col")
.limit(1)[0]
.json_col
I have a table of institutes and a table of locations. I extracted the data from spreadsheets and currently have the following information:
Institute
id, name, ukprn
Location
id, name, ukprn, lat, long, institute
ukprn is the unique id given through the government, but some institutes in the future may not have this so I don't want to specifically use it as a reference. What I think I need to do is for the Location.institute attribute to contain the Institute.id where Institute.ukprn and Location.ukprn match, but I'm not sure how this would work and save it to the code.
I tried:
Location.each do |location|
if location.ukprn == Institute.ukprn then
put Institute.id => Location.institute
end
end
This comes up with an undefined method 'each' error. I'm clearly doing something wrong, but not sure how to go about this.
You are getting undefined method each error because Location as a model class don't have each method. There are other things which are wrong in your code.
You have to do this in a following way,
Institute.find_each do |institute|
Location.where(ukprn: institute.ukprn).update_all(institute_id: institute.id)
end
In above code for each institute you are checking for ukprn corresponding to all locations, every matched location's institute_id will be updated with institute id.
It doesn't make sense to call each method on a class. First get all the locations and institutes and do something like below:
#locations = Location.all
#institutes = Institute.all
#locations.each do |location|
#institutes.each do |institute|
if location.ukprn == institute.ukprn
institute.id = location.institute
institute.save!
end
end
end
I guess there might be a better way to optimize the lookup by not using each to loop over each record, e.g., you can delete/mark a record as "processed" and then later get just get the "unprocessed" records so your array size will reduce in the subsequent each loop.
hope it helps! let me know if you are looking like this.
I want create a simple checking value from database. Here is my code:
def check_user_name(name, email)
db_name = Customers.find_by_name(name).to_s
db_email = Customers.find_by_email(email).to_s
if name == db_name && email == db_email
return 'yes'
else
return 'no'
end
end
But I have allways 'no' variant....why ?
Because you are calling to_s on your Customers model and not actually getting the name. The two fetch lines you have should be:
Customers.find_by_name(name).name.to_s # to_s probably not necessary if you know this field is a string
Customers.find_by_email(email).email
But, you're making two separate requests to the database. I don't know what the purpose of this is (as you could be selecting two different Customers) but you could do:
if Customers.where(name: name, email: email).exists?
"yes"
else
"no"
end
Since you are, however, selecting by name and email - I would highly recommend that you make sure those fields are indexed because large tables with those requests will bog the server and make that route rather slow (I would actually recommend that you pursue other routes that are more viable, but I wanted to mention this).
When you give Customers.find_by_name(name), you will not get name of a customer. Actually it will return activerecord object, so from this object you need to get the name and email of a customer, like below,
name = Customers.find_by_name(name).name
email = Customers.find_by_email(email).email
Now you will get the exact name and email of matched data from DB.
Is there any better way to achieve this in Ruby on Rails?
I'm searching for 11 fields and all are required also, and if not found initialize it.
There will be more required fields adding to it.
This query works perfect for me, but it just doesn't look like the best way to do this.
find_or_initialize_by_make_and_country_and_engine_and_power_and_body_and_doors_and_fuel_and_cylinders_and_transmission_and_gears_and_wheels(model,country,engine,power,body, doors, fuel, cylinders, transmission,gears,wheels)
On rails 3.2 I would do
attributes = {}
Model.where(attributes).first_or_initialize
Documentation http://apidock.com/rails/ActiveRecord/Relation/first_or_initialize
Considering the sheer number of fields you are using, you probably are better off manually finding the record and initializing it if it does not exist:
attributes = {
country: country,
engine: engine,
power: power,
body: body
etc ...
}
record = where(attributes).first
record = new(attributes) unless record
You can define method like this in your model
def self.find_or_initialize_by_field(params)
send("find_or_initialize_by_#{params.keys.join("_and_")}", *params.values)
end
and then you can call this method like
YourModel.find_or_initialize_by_field({country: country, engine: engine})
while using find_or_intialize_by
find by key fields , dont assign all the feilds at once
eg I assume the make is the key field in the model
car = find_or_initialize_by_make(model)
car.update_attributes(country: country,engine: engine,power: power, body: body,doors: doors ,fuel: fuel,cylinders:cylinders,transmission: transmission, gears: gears, wheels: wheels)
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Dynamic+attribute-based+finders
I have noticed with the Google Maps For Rails gems which otherwise works perfectly that when I change an address in the model field the coordinates are not automatically updated, even though the address field is updated and saved. I have created an before_save method that calls geocode.
before_save :update_location_coordinates # in model being mapped
protected
def update_location_coordinates
place = Gmaps4rails.geocode(gmaps4rails_address).first
self.longitude, self.latitude = place[:lng], place[:lat] unless place.empty?
rescue
nil
end
This works but I am wondering if it is necessary as it seems like something that should be automatic in the gem. Am I missing something?
thanks...
PS geocode returns an array so I just took the first (best) guess
The refresh of the coordinates is a tricky process because of the gmaps4rails_address method. It's flexible so easy to use but the counterpart is it's not possible to know if the address really changed.
That's why I give coders two different opportunities to fit their needs:
If you don't care about performance, you could refresh the coordinates every time the model is saved (and then even if the address hasn't changed). See here: https://github.com/apneadiving/Google-Maps-for-Rails/wiki/Model-Customization.
Change :check_process to false:
acts_as_gmappable :check_process => false
2. If you want to have full control over the process, set the gmaps boolean to false whenever you want the coordinates to be updated. It could be a hidden field in your form or a hook in your model checking the necessary fields.
This is an economic way to solve the problem:
add a column to the model - eg last_g4r_address
add a before_validation function:
def check_g4r_address
self.gmaps = false if last_g4r_address != gmaps4rails_address
self.last_g4r_address = gmaps4rails_address
end
This way the geocoding is updated only if the address changed.