Check if record exists in Rails before creating - ruby-on-rails

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.

Related

AcitveRecord where method that matches just one record

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.

How to refer to the current record in a Rails query?

In a Rails controller I'm using a JSON request to an external database to return integers that are used to order a collection of records:
Model.order(JSON.parse(open("http://myapp.com/models/#{:id}")).read)['attribute'])
I want to make this dynamic to aid switching between environments. Something like:
Model.order(JSON.parse(open(model_url(model))).read)['attribute'])
This is not correct, and model_url(model) is returning an error:
undefined local variable or method 'model'
How do I refer to self in query?
There must be a more elegant solution than
...JSON.parse(open("#{root_url}/models/{:id}"))....
EDIT:
Lightswitch05's answer below does anser the question I asked. The query should reference params[:id] to get the url of the current record.
In fact, I have decided to move this JSON call into a virtual attribute on the model. This means I can simply call Model.order(:my_virtual_attribute). While this solution brings its own share of issues—I needed to make url_helpers available to the model—in the long run I think this will be a cleaner solution.
As per our discussion the problem is that model is not defined. Since all you are trying to do with model is get the url to it, all you really need is params[:id]. This will fix your error message:
Model.order(JSON.parse(open(model_url(params[:id]))).read)['attribute'])
where model has been replaced with params[:id]
Change your code:
Model.order(JSON.parse(open(model_url(model))).read)['attribute'])
to:
model_tableized = Model.to_s.tableize
model_url = "#{model_tableized}_url(#{model_tableized.chomp('s')})"
Model.order(JSON.parse(open(model_url).read)["attribute"])
I think that should work.
You need the name method.
If you're using the model, and not an instance of the model, it would be Model.name
If you're using an instance, it would be
#model = Model.first
#model.class.name

Using Rails update_all method to update fields with value from another column

I'm trying to update a field in using update_all. However I need the value to be taken from another field which is re-written to my specific format.
If I have something like this in my model:
def self.clean_mac_address()
clean_mac_address = :macaddress.gsub(/[^0-9a-z]/i, '')
end
When I run this:
Radacct.update_all("mac_clean = #{clean_mac_address}")
I get an error:
NoMethodError: undefined method `gsub' for :macaddress:Symbol
Any thoughts how I can do this? Or is there a simpler way to update the field?
update_all generates a single SQL query to run - it can't do clever stuff like change arbitrary bits of ruby into equivalent SQL.
You either need to load all you instances (via find_each for example) and fix them one by one (ie don't use update_all), for example
Foo.find_each do |foo|
# update foo here
foo.save!
end
Or find a way of expressing that cleaning operation in SQL. For example Postgres has a regexp_replace function
Foo.update_all("some_column = regexp_replace(some_column, 'your_regexp_here', '','g')")
Which would remove everything replacing that regexp. Obviously you'll need to check the documentation for your database to see whether it supports such a feature.
While the accepted answer provides a nice way to update_all, what I'd use is
read_with_clean_addr = Radacct.where(mac_clean: :macaddress.gsub(/[^0-9a-z]/i, ''))
read_with_clean_add.update_all(mac_clean: "#{clean_mac_address}")

Can I access the collection an instance method was called on in ruby on rails

I'm working on implementing a search form in a ruby on rails application. The general idea is to use form_tag to submit the search fields (via params) to a search function in the model of the class I'm trying to search. The search function will then iterate through each of the params and execute a scoping function if the name of the function appears in params.
The issue is that when I call the search on a collection like so:
#calendar.reservations.search({:search_email => "test"})
I don't know how to refer to the collection of #calendar.reservations from within the search function.
Additionally I'm confused as to why #calendar.reservations.search(...) works, but Reservations.all.search gives me an error saying you can't call an instance method on an array.
I've got the details of the search method over here: https://gist.github.com/783964
Any help would be greatly appreciated!
I don't know how to refer to the
collection of #calendar.reservations
from within the search function.
If you use self (or Reservation, it's the same object) inside the classmethod, you will access the records with the current scope, so in your case you will see only the reservations of a particular calendar.
[edit] I looked at you search function, and I think what you want is:
def self.search(search_fields)
search_fields.inject(self) do |scope, (key, value)|
scope.send(key, value)
end
end
Additionally I'm confused as to why
#calendar.reservations.search(...)
works, but Reservations.all.search
gives me an error saying you can't
call an instance method on an array.
#calendar.reservations does not return a standard array but a (lazy) AssociationCollection, where you can still apply scopes (and classmethods as your filter). On the other hand Reservation.all returns a plain array, so you cannot execute search there (or any scope, for that matter).
You don't really need a search method at all, as far as I can tell.
Simply use where:
#calendar.reservations.where(:search_email => 'test')
I would strongly encourage you to look at the MetaSearch GEM by Ernie Miller. It handles the kind of thing you're working on very elegantly and is quite easy to implement. I suspect that your view code would almost accomplish what the GEM needs already, and this would take care of all your model searching needs very nicely.
Take a look and see if it will solve your problem. Good luck!
Reservation.all.search doesn't work because it returns all the results as an array, while Reservation.where(..) returns an ActiveRecord object (AREL). Reservation.all actually fetches the results instead of just building the query further, which methods like where, limit etc do.

find_or_create_by_facebook_id method not found

I'm trying to find out where this function comes from. Any one have any clue? It's used by this:
http://github.com/fluidtickets/facebooker-authlogic-bridge
with a working example here:
http://facebooker-authlogic-bridge.heroku.com
Downloading the code, it throws: undefined method 'find_or_create_by_facebook_id' for #<Class:0xb04dd1c>
I have no clue where this function comes from.
Thanks all!
ActiveRecord creates dynamic finders based on columns in your database. So for example if you have a user with a username attribute then activerecord creates a number of dynamic finders:
find_by_username
find_or_initialize_by_username #=> calls new if not found
find_or_create_by_username #=> calls create if not found
You can string a few attributes together like
find_by_username_and_email
To get back to your problem - I suspect that you haven't run some required migration that adds the facebook_id to your users table in the db and therefore ActiveRecord isn't adding the dynamic finder for facebook_id to the class.

Resources