Case1: Creating a new record for a model with assigning the associated object(Status object instead of Status.id) like
visit = Visit.create(:date => Date.today, :status => Status.first)
visit.status_id #=> 1
After creating the record
I'm trying to update the attribute 'status_id' (Status.id instead of Status object) but it returns the old value even calling reload after save the object. Example:
visit.status_id = Status.last.id
visit.save
visit.reload
visit.status_id #=> 1
When I calling reload method after create it is working as expected
visit = Visit.create(:date => Date.today, :status => Status.first)
visit.status_id #=> 1
visit.reload
visit.status_id = 2
visit.save
visit.status_id #=> 2
Case2: If I assigning the 'status_id' value instead of Status object, then no need to reload object and its working fine
visit = Visit.create(:date => Date.today, :status_id => Status.first.id)
visit.status_id #=> 1
visit.status_id = 2
visit.save
visit.status_id #=> 2
Using the two cases above, when should I use the reload method? Should it be before or after save?
I think your understanding of how save and reload work is basically correct. Your results certainly seem to be inconsistent. I have a feeling based on your results that your save is failing.
save will try to store the local version to the database. It does not refresh any values from the database if the model has changed since you loaded it. If the save fails, the local values are still whatever you set them to, but the database will have the old values.
reload overwrites any local changes you have made and sets them to the database values.
Assuming there is no contention on the database for the models you are concerned with, let's walk through your examples.
Example 1
If the save works, then the reload will not do anything and the value of visit.status_id will be Status.last.id. However, if the save fails then the value of visit.status_id will be whatever it was in the database originally.
Example 2
If the create does not work then the reload would give a RecordNotFound error because you can't reload something that hasn't been persisted. After that you set the status_id to 2 and then call save. Whether the save succeeds or fails, the local value of status_id will remain as 2. However, the database value depends on whether it passed or failed.
Example 3
Hey! This is the same as example 2 except for the reload.
Here is some documentation, but I'm not sure how helpful you will find it:
http://apidock.com/rails/ActiveRecord/Persistence/save
http://apidock.com/rails/ActiveRecord/Persistence/reload
You might want to check for save success or call save! to ensure the save is actually working.
I hope that helps.
Related
In Rails 5, what is the difference between update and update_attributes methods. I'm seeing the following results for both the methods
Returns true/false
Checking for active record validation
Call backs are triggered
and also regarding update method a new thing was introduced in active record relation. I'm not able to understand it. What is the difference?
Moreover are we using update_attributes in Rails 5. It's not there in active record documentation.
I'm confused with all update methods. I need clarity
As of Rails 4.0.2, #update returns false if the update failed. Before Rails 4.0.2, #update returned the object that got updated. The main difference therefore was the return value. After this change, #update_attributes is just an alias of #update. It seems there are talks to deprecate #update_attributes in Rails 6 which is not released yet.
https://github.com/rails/rails/pull/31998
https://github.com/rails/rails/commit/5645149d3a27054450bd1130ff5715504638a5f5
From the rails 5 files it seems to me update can be used to update multiple objects(array of records) but update_attributes only work on single records otherwise both are same
From rails core files for update_attributes:
Updates a single attribute and saves the record.
This is especially useful for boolean flags on existing records. Also note that
Validation is skipped.
\Callbacks are invoked.
updated_at/updated_on column is updated if that column is available.
Updates all the attributes that are dirty in this object.
This method raises an ActiveRecord::ActiveRecordError if the
attribute is marked as readonly.
def update_attribute(name, value)
name = name.to_s
verify_readonly_attribute(name)
public_send("#{name}=", value)
save(validate: false)
end
For Update
Updates an object (or multiple objects) and saves it to the database, if validations pass.
The resulting object is returned whether the object was saved successfully to the database or not.
==== Parameters
+id+ - This should be the id or an array of ids to be updated.
+attributes+ - This should be a hash of attributes or an array of hashes.
==== Examples
# Updates one record
Person.update(15, user_name: "Samuel", group: "expert")
# Updates multiple records
people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
Person.update(people.keys, people.values)
# Updates multiple records from the result of a relation
people = Person.where(group: "expert")
people.update(group: "masters")
Note: Updating a large number of records will run an UPDATE
query for each record, which may cause a performance issue.
When running callbacks is not needed for each record update,
it is preferred to use {update_all}[rdoc-ref:Relation#update_all]
for updating all records in a single query.
def update(id, attributes)
if id.is_a?(Array)
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
object.update(attributes[idx])
}
else
if ActiveRecord::Base === id
raise ArgumentError,
"You are passing an instance of ActiveRecord::Base to `update`. " \
"Please pass the id of the object by calling `.id`."
end
object = find(id)
object.update(attributes)
object
end
end
When we are working with update_column that time update is done on the database level there is no any contact with the rails ORM so whatever logic we have implemented like callbacks and validations all will be waste and wont be useful as this is going to be bypassed.
I found this article explained really well in just 30 seconds.
.update
Use update when you want to return false, for example in an if/else:
if record.update(params)
display_success
else
react_to_problem
end
.update!
Use update! when you want an error (for example: to avoid erroring silently, which could be very bad if an error was unexpected and you needed to know about it to fix it!):
record.update!(params) # raises is invalid
'update' respects the validation rules on model, while 'update_attributes' ignores validations.
Is it possible to change the value in the Params hash when a Javascript function is called?
I have a hidden Div, say DIV1 that becomes visible based on the selected value in a select field, within DIV1, I have a readonly textfield whose value is set to a value returned by a helper method.
This helper method uses a dynamic find_by that depends on the value of Params,but I guess the param Hash doesn't change when the value of the select Changes (since it isn't a full page refresh?). Please, how do I Achieve updating this so that when the select Value changes, the new value is reflected in the params hash. I have :remote=>true in the form_for tag. Is there a better approach than mine?
The Select field in a rails view
#finance_year
<%=f.select :financeyear, options_for_select(finance_year),{ :include_blank => 'Select a
Financial Year' } %>
and a an onchange event for that select
jQuery ->
$('#finance').hide()
value = "Select a Financial Year"
$('#finance_financeyear').change ->
selected = $('#finance_financeyear :selected').text()
$('#finance').show()
$('#finance').hide() if selected is value
the helper Method
def amount_owed(student)
financeyear = params[:financeyear]
#thisstudent = Finance.find_last_by_user_id(#student.user_id,
:conditions => {:financeyear => financeyear } )
if(#thisstudent)
#amount_owed= #thisstudent.amount_owed
else
student.department.amount
end
end
I appreciate any help and I hope I've been able to ask the question intelligently.
The answer is AJAX.
First, we'll need to add a new route to config/routes.rb to make amount_owed() a true action:
get '/finance_years/amount_owed/:student_id/:financeyear' => "finance_years#amount_owed"
Next, we'll create a default view to be returned whenever the amount_owed() action is called:
/app/views/finance_years/amount_owed.html.erb
<%= #amount_owed %>
So, that part was easy. Now we need to edit the amount_owed action so it will work with our parameters:
/app/controllers/finance_years_controller.rb
def amount_owed(student)
financeyear = params[:financeyear]
#thisstudent = Finance.find_last_by_user_id(params[:student_id],
:conditions => {:financeyear => financeyear } )
if(#thisstudent)
#amount_owed= #thisstudent.amount_owed
else
#amount_owed = student.department.amount
end
end
This way, we can pass in the finance year and the student id from the params hash and get an amount_owed every time. Now, to give our coffeeScript access to the current student_id and finance_year variables, I'd add a couple hidden fields to the form in the view file:
/app/views/finance_years/_form.html.erb
<%= hidden_field_tag :student_id, #student_id %>
<%= hidden_field_tag :finance_year, #finance_year %>
The last trick is to edit the coffeeScript, firing an asynchronous GET request whenever the select box changes.
/app/assets/javascripts/finance_years.js.coffee
$('#finance_financeyear').change ->
student_id = $("#student_id").val()
finance_year = $("#finance_year").val()
selected = $('#finance_financeyear :selected').text()
$('#finance').show() unless selected == value
$.get "/finance_years/amount_owed/#{student_id}/#{finance_year}", (response)->
$('#finance input[type=text]').load(response)
And that's about all I can do being away from my rails development machine. Let me know if additional problems arise.
I'm not a rails expert, but you're not going to be able to modify server side code directly from javascript. You will need to make a call down to the server (either on a form submit or through an ajax request) to tell the server to update itself.
To clarify, the server code is responsible for the initial rendering of the page, but once the template has been rendered it doesn't exist on the client page. So you can't directly modify it from coffeescript/javascript. You need to send a request back to the server to handle that.
Blockquote
We are using the ByCode method of mapping our data..
I have Process (Process table) object which has a List of ProcessStep (ProcessStep table) objects which in turn has a Bag of ProcessStepUser (ProcessStepUser table) objects assigned to each step.
The objects load just fine, but in the web page if I delete a Step from the collection, rehydrate into MVC action as a Process and then save the process, the Step in the database simply has the ProcessId set to null which breaks the link, but leaves the Step in the Database and all its assigned users.
What I want to be able to do is delete a Step and have its ProcessStep and all its ProcessStepUsers deleted.
Likewise, when I edit a ProcessStep (say I change the name of the step) and save, it's saving that, but the ProcessStep users are nulled out (of their ProcessStepId) and recreated, leaving orphaned records.
I do have a column on ProcessStepUser for overall ProcessId as well, so that I can prevent a User from being assigned more than once to any step of the process.
My relevant mapping is as follows:
Process:
List(x => x.ProcessSteps, m =>
{
m.Key(k => k.Column("ProcessId"));
m.Index(i => i.Column("StepOrder"));
m.Lazy(CollectionLazy.NoLazy);
m.Cascade(Cascade.All);
m.Inverse(false);
},
r => r.OneToMany(o => o.Class(typeof (ProcessStep))));
ProcessStep:
Bag(x => x.ProcessStepUsers, m =>
{
m.Key(k => k.Column("ProcessStepId"));
m.Lazy(CollectionLazy.NoLazy);
m.Cascade(Cascade.All);
m.Inverse(false);
},
r => r.OneToMany(o => o.Class(typeof (ProcessStepUser))));
ManyToOne(x => x.Process, m =>
{
m.Column("ProcessId");
m.Class(typeof (Process));
});
ProcessStepUser
ManyToOne(p => p.Step, m =>
{
m.Column("ProcessStepId");
m.Class(typeof (ProcessStep));
});
ManyToOne(p => p.Process, m =>
{
m.Column("ProcessId");
m.Class(typeof (Process));
});
As I said, it saves just fine on creation and loads just fine for display. But the dropping of a step or editing of a step is creating havoc in the Database.
I hacked the step deletion process by flagging them and manually deleting them before saving the Process master object, but I'd like nhibernate to do it all if possible.
Thanks!
Blockquote
First off thanks to Martin who caught the one issue with the Cascade.All.Include(Cascade.DeleteOrphans)... that was the original code I had, I forgot I'd changed it while attempting to solve the problem.
I rewrote some tests and the mapping actually is working just fine, so apologies there. I would delete this question, but the site is not letting me to anything (in Firefox) but edit...
The problem ended up being because I was getting the Process object from the database and then serializing it up to the View as a JSON object. I then passed back the JSON object which was bound back to the Process object.
The long and short is that our repository was calling SaveOrUpdate(obj) on the object, but since it had been disconnected, we needed to call Merge(obj) which we did and worked perfectly.
I leave it to the powers that be as to whether this question has any value remaining.
Thanks!
Try Cascade(Cascade.AllDeleteOrphan) on your associations where you want them to be deleted instead of having the other side nulled...
I'm attempting to write a site in Rails where a user in a manufacturing plant can see what devices are failing. The program storing the alarm data stores one entry when a device faults, and then stores another entry when the device gets fixed. The entries are linked only by having the same value in the EventAssociationID column. How might I write a named scope in Rails to check which faults have been fixed and which ones haven't?
I wasn't able to do it in a named scope, however, I was able to define a method for the model that solved the problem:
def inAlarm
return ConditionEvent.count(:all, :conditions => ['EventAssociationID = ?', self.EventAssociationID]) == 1
end
I was wondering what the best implementation for displaying a warning for a particular field being sent to the database.
To give you an example, somebody provides data which is considered valid, but questionable. So we want to treat it as if it was a regular validation error on the first go and confirm that it's what the user actually wants to enter. At this point they will have the option to either continue or change the data being entered. If they choose to continue they'll be given the go-ahead and we'll skip that validation on the next run-through.
However (and this is the part I'm not sure about), if they change that field to another value that can be considered questionable we want to take them through the same process. Keep in mind these are new records and not records that have already been persisted to the database.
Can such a feat be accomplished with basic conditional validations? Would there be a better option?
Just to clarify my application knows exactly how to handle this questionable data, but it's going to be processed differently than normal data and we just want to inform the user ahead of time with a warning.
Currently the validation is your typical custom validation method that dictates the validity of an object.
validate :some_field_some_rules
def some_field_some_rules
if some_conditions_must_be_true
errors.add(:some_field, "warning message")
end
end
Edited, let's try with a custom validation that will be triggered only when you need to.
class MyModel < ActiveRecord::Base
attr_accessor :check_questionable
validate :questionable_values_validation, on: :create, if: Proc.new { |m| m.check_questionable }
def initialize
check_questionable = true
end
private
def questionable_values_validation
if attribute1 == "Questionable value"
self.errors[:base] << "Attribute1 is questionable"
check_questionable = false
end
end
end
Then, when you render the create form, be sure to add an hidden_field for check_questionable :
f.hidden_field :check_questionable
So the first time, when calling the create action, it'll save with check_questionable = true. If there's a questionable value, we add an error to ActiveRecord standard errors AND set the check_questionable to false. You'll then be re-rendering the new action but this time with the hidden_field set to false.
This way, when the form is re-submitted, it won't trigger questionable_values_validation ...
I didn't test it, might need some tweak, but it's a good start I believe!