Better way to check If model is exsiting then update it - ruby-on-rails

Because I have unique key for the model,
If a new record trying to create with a existed ** unique key**
It should be fail.
But I want the behavior be that
If (Create file) Then (Update the exsited record).
begin
Model.create(DATA)
rescue Exception => e
Model.update(DATA)
end
Is there any side-effect for my code ?

You can use the find_or_initialize_by method:
Model.find_or_initialize_by(key: :my_key) do |model|
model.attr_1 = "New attribute 1."
model.attr_2 = "New attribute 2."
model.save
end
Also notice that it's generally not considered a good practice to rescue Exception. You should probably be rescuing ActiveRecord::RecordInvalid or ActiveRecord::ActiveRecordError instead, or at the very least StandardError.

The answer depends a bit on your Rails version. In Rails 4 you would do something like this:
ModelName.where(attributes_hash).first_or_create
see Documentation: http://apidock.com/rails/ActiveRecord/Relation/first_or_create

Related

What is the use of ! in rails

What is the use of ! in rails?
Especially in this line: From HArtl tutorial
users = User.order(:created_at).take(6)
50.times do
content = Faker::Lorem.sentence(5)
user.each { |user| user.microposts.create!( content: content )}
end
Basically this is creating tweets/microposts for 6 users.
I am really wondering why need to use !
The important thing to remember is that in Ruby a trailing ! or ? are allowed on method names and become part of the method name, not a modifier added on. x and x! and x? are three completely different methods.
In Ruby the convention is to add ! to methods that make in-place modifications, that is they modify the object in fundamental ways. An example of this is String#gsub which returns a copy, and String#gsub! which modifies the string in-place.
In Rails this has been ported over to mean that as well as situations where the method will raise an exception on failure instead of returning nil. This is best illustrated here:
Record.find_by(id: 10) # => Can return nil if not found
Record.find_by!(id: 10) # => Can raise ActiveRecord::RecordNotFound
Note that this is not always the case, as methods like find will raise exceptions even without the !. It's purely an informational component built into the method name and does not guarantee that it will or won't raise exceptions.
Update:
The reason for using exceptions is to make flow-control easier. If you're constantly testing for nil, you end up with highly paranoid code that looks like this:
def update
if (user.save)
if (purchase.save)
if (email.sent?)
redirect_to(success_path)
else
render(template: 'invalid_email')
end
else
render(template: 'edit')
end
else
render(template: 'edit')
end
end
In other words, you always need to be looking over your shoulder to be sure nothing bad is happening. With exceptions it looks like this:
def update
user.save!
purchase.save!
email.send!
redirect_to(success_path)
rescue ActiveRecord::RecordNotFound
render(template: 'edit')
rescue SomeMailer::EmailNotSent
render(template: 'invalid_email')
end
Where you can see the flow is a lot easier to understand. It describes "exceptional situations" as being less likely to occur so they don't clutter up the main code.

update_attribute not doing anything after first_or_create if record does not exist previously

I do a first_or_create statement, followed by a update_attributes:
hangout = Hangout.where(tour: tour, guide: current_user).first_or_create
hangout.update_attributes(priority: current_user.priority)
If the record already existed, it updates the priority. If it doesn't exist previously, there is no update. Why?
Thanks!
update_attributes (aka update) returns a boolean indicating if there was an error, if you do not check it - use bang-version update! so that exception will not be ignored.
Most probably record does not get created due to validation. Also when you're updating new record just after create - better use first_or_create(priority: current_user.priority) or first_or_initialize(with subsequent update) to spare extra DB write.
def update_attributes!(attributes)
self.attributes = attributes
save!
end
update attribute with bang call the save with bang.
def save!(*args, &block)
create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
end
Inside the save! RecordNotSave error will be raise if it cannot save the record.
so you can customize the error handing from your code.
begin
hangout.update_attributes!(priority: current_user.priority)
rescue RecordNotSaved => e
#do the exception handling here
end

Updating database records in Rails

I'm new to RoR and I'm now confusing about updating data to databases.
And I studied RoR just recently, if the question is unclear please let me know.
Assume I created a table "book", with 3 columns: "name", "price", and "author".
When implementing action update in rails, I'll do the following:
def update
#book = Book.find(params[:id])
if #book.update({'name': 'Ruby tutorial'})
redirect_to #book
else
render 'edit'
end
end
This will update the record in database by
if #article.update({'name': 'Ruby tutorial'})
In order to test some failure cases, I modified the database column "name" to "nane" on purpose,
I thought the statement if #article.update will fail due to the wrong table field name and the code will go to else block.
Because I thought the statement is for checking whether rails saves the record into the database successfully.
However, my code throws exception because of the wrong field name instead of going to the else block.
I got confused about this behavior, what kinds of situation will cause
if #article.update(params) fail and go to the else block?
Is if #article.update(params) for just validating form data? i.e, checking whether the hash key sent from the form is correct, as for database side(field name or database connection error) , is not the business of this statement so it will throw exception.
Thanks a lot.
#book.update(nane: 'name')
will throw an exception ActiveModel::UnknownAttributeError: unknown attribute.
if is not an exception handler.
The purpose of using if and else block here is to handle the negative scenario (update failed due to some validation failures).
update(name: '')
with presence validation on name column will not throw an exception. It will return false. If you do not have the if / else block here and simply update statement, you will not know whether the update has succeeded or not.
The other way of handling this is to use update with !. Adding ! makes the statement to throw an exception for any failures.
begin
update!(name '')
rescue Exception
render 'edit'
end
Rescuing exception like above is a bad style of handling exception.I have removed
rescue Exception
//some action //
end
You must learn why from here Why is it bad style to `rescue Exception => e` in Ruby?.
You can also handle specific RecordInvalid exception like this.
begin
update!(name '')
rescue ActiveRecord::RecordInvalid => invalid
// some action //
end

Using an if statement with Rails ActiveRecord find method

I want to check that a user is included in a group. In an attempt to do this, I have the following statement:
user_id = current_user.id
unless (group.user.find(user_id))
redirect_to different_path
end
Looking at the docs, "A failure to find the requested object raises a ResourceNotFound
exception if the find was called with an id." How do I write the unless statement so that it operates properly?
If you defined a relation between group and users, you can call current_user.group.present? This will return false if there is no related object.
You can handle the redirect as part of Exception handling
redirect_to path if group.user.find(user_id)
rescue ResourceNotFound
redirect_to differenct_path
end
alternatively and probably a better way would be to build your logic around
user.groups.include?(group)

Soft db error handling for duplicate entries? -- Rails 3.1 newbie

If a user tries to enter a duplicate entry into a table, they get a full page nasty error
ActiveRecord::RecordNotUnique in Admin::MerchantsController#add_alias
Mysql2::Error: Duplicate entry '2-a' for key 'merchant_id': ..
Is there a way to give a flash message instead like "This record already exists"?
This thread from railsforum can help you
I wouldn't recommend checking for the uniqueness and specifically responding to this validation rule via a flash message, this conventionally should be handled by the form's error messages instead.
Nonetheless, in the controller actions for creating and updated you could wrap the conditional block which decides where to send the user, with a check for uniqueness first:
def create
#merchant = Merchant.new params[:merchant]
if Merchant.where(:merchant_id => #merchant.id).count > 0
flash[:error] = "The merchant id #{#merchant.id} already exists"
render :create # amend this to :update when applying to the update action
else
# your normal generated save and render block
end
end
This isn't the cleanest way of achieving your goal, but I think it'll be easiest to understand.
Would really recommend the model validations and form error messages instead, which if you are usung the generated scaffolding, all you need to do is add a model validation and the form throw out the error messages to the user for you:
# app/models/merchant.rb
class Merchant < ActiveRecord::Base
validates_uniqueness_of :merchant_id
end

Resources