ActiveRecord caching and update_attributes - mysql

If a model changes an attribute locally, then changes it back, ActiveRecord doesn't send the change to the DB. This is great for performance, but if something else changes the database, and I want to revert it to the original value, the change doesn't take:
model = Model.find(1)
model.update_attribute(:a, 1) # start it off at 1
# some code here that changes model.a to 2
model.a = 2 # I know it changed, reflecting my local model of the change
model.update_attribute(:a, 1) # try change it back, DOESN'T WORK
The last line doesn't work because AR thinks in the DB it's still 1, even though something else changed it to 2. How can I force an AR update, or update the cache directly if I know the new value?
Side note: the code that changes it is an update_all query that locks the record, but it has side effects that mess up the cache. Multiple machines read this table. If there's a better way to do this I'd love to know.
Model.update_all(["locked_by = ?", lock_name], ["id = ? AND locked_by IS NULL", id])

Use the reload method for this.
model.reload(:select => "a")
OR
You can try the will_change! method(Its not clear how your change happens. But you can try this method).
model.update_attribute(:a, 1) # start it off at 1
model.a_will_change! #fore warn the model about the change
model.a = 2 #perform the change
model.update_attribute(:a, 1)

The answer by Harish Shetty is correct, you need to call reload on the reference, however I found a better way to do that automatically.
In your model you want to reload attribute to, create a after_update callback and call reload directly there, like so:
after_update :reload_attr
def reload_attr
reload select: "attr"
end

Related

SQLAlchemy not marking session as dirty for changed attribute

When attempting to change an object loaded in with SQLAlchemy, the session.dirty object is not behaving the way I'd expect:
o = sqla.session.query(sqla.Game).first()
<sqla.Game at 0x7fdcc1f707b8>
o.wiki
<null>
o.wiki = 'test'
o.wiki
'test'
sqla.session.is_modified(o)
False
sqla.session.dirty
IdentitySet([])
inspect(o).attrs['wiki'].history
History(added=(), unchanged=['test'], deleted=())
Committing this to the database does in fact update it, but I'm really unclear on why it's marked as "unchanged". If I modify a relationship on the object, that does properly show in the "new" and "deleted" areas in history. I'm loading the models in via automap, and the session does not have autocommit on.
I have also tried manually calling flag_modified, and specifying the column directly (without automap) to no avail.
Annnd I figured it out. I had autoflush on by default. Turning it to False fixed the issue.

Django save/update not change data in database

Django 1.11.7
MySQL
I was trying to change the value of an object like this:
# change the value of the filed and save
def patch(...):
instance.field_name = new_name
instance.save()
print(instance.filed_name)
When I run the code I got the print result as new_name. But when I check the database manually I got the result as old_name.
Then I tried ways like:
instance.save(update_fields=['field'])
and
ModelName.objects.filter(id=instance.id).update(field_name=new_name)
but get the above problem as well. And meanwhile, the project runs perfectly functional except for this segment of code.
Any idea what caused this problem or suggestion on how to solve it?
Is that piece of code inside a transaction? Maybe the transaction gets rolledback somewhere later.
When you read from the DB are you inside a transaction? Some transaction modes may not show you this change.
Are you sure that field_name is the correct field name? Maybe you have a typo and you just set a property of the object without changing model field. From what I see you sometimes type "field_name" and sometimes "filed_name"

ActiveRecord model column not updating (even though save! succeeds)

I've got a really, really odd problem manifesting on a big Rails e-commerce app and thought I'd see if anyone has good insight. I have an"Order" model with many associations. If I create a new instance, and then set one particular column value and "save!" the "save!" is succeeding without errors, but the change isn't actually persisted to the DB. I'll run through the scenario below:
#order = Order.create!(<some attributes>)
=> true
#order.shipping_method_id
=> 1
#order.shipping_method_id = 203
=> 203
#order.save!
=> true
#order.shipping_method_id
=> 1
To try and debug this I actually prepended a before_save filter and I can see that when this first filter is called after setting the value, it is correct ("203") BUT the very next before_save after the 6-or-so built-in "autosave_foo_bar_quux" filters (for nested associations) it is back to "1".
Oddly, if I just reload the order (#order.reload), change the column value and save! the update does succeed.
In both cases, doing #order.changed shows that ActiveModel recognizes the column value change for shipping_method_id. In the first, though, the SQL logging shows that the order row is not updated.
I feel like I'm going insane. Any ideas? Also, let me know if there's anything else I can post here for more context.

Do views immediately reflect data changes in their underlying tables?

I have a view ObjectDisplay that is composed of two relevant tables: Object and State. State represents the state of an Object, and the view pulls some of the details from the most recent State for each Object.
On the page that is displaying this information, a user can enter some comments, which creates a new State. After creating the new State, I immediately pull the Object from ObjectDisplay and send it back to be dropped into a partial view and replace the Object in the grid on the page.
// Add new State.
db.States.Add(new State()
{
ObjectId = objectId,
Comments = comments,
UserName = username
});
// Save the changes (executes all of the above).
db.SaveChanges();
// Return the new Object information.
return db.Objects.Single(c => c.ObjectId == objectId);
According to my db trace, the Single call occurs about 70 ms after the SaveChanges call, and it occurs on the same SPID.
Now for the issue: The database defaults the value of RecordDate in State to GETUTCDATE() - I don't provide the date myself. What I'm seeing is that the Object returned has the State's RecordDate of the old State and the Comments of the new State information of the old State. I am seeing that the Object returned has the old State's information. When I refresh the page, all the correct information is there, but the wrong information is returned in the initial call from the database/EF.
So.. what could be wrong? Could the view not be updating quickly enough? Could something be going on with EF? I don't really know where to start looking.
If you've previously loaded the same Object entity in the same DbContext, EF will return the cached instance with the stale values, and ignore the values returned from SQL.
The simplest solution is to reload the entity before returning it:
var result = db.Objects.Single(c => c.ObjectId == objectId);
db.Entry(result).Reload();
return result;
This is indeed odd. In SQL Server views are not persisted by default and therefore show changes in the underlying data right away. You can create a clustered index on a view with effectively persists the query, but in that case the data is updated synchronously, so you should see the change right away.
If you are working with snapshot isolation level your changes might not be visible to other SPIDs right away, but as you are on the same SPID and do not use snapshot isolation, this cant be the culprit either.
The only thing left at this point is the application layer. Are you actually using the result of the Single call higher up in the call stack or does that get lost somewhere. I assume that a refresh of the page uses a different code path, which would explain why it is working there.

Linq update with explicit operator

I am trying to do an update with linq using an explict cast and the changes arent submitting.
Here is the code
Image update = db.Images.Where(i => i.ImageId == imageWithChanges.ImageId).SingleOrDefault();
update = (Image)imageWithChanges;
db.SubmitChanges();
I have an explicit operator in my image class. Can anyone help?
Thanks
The line
update = (Image)imageWithChanges;
is not changing anything. It's merely swapping the thing the variable update points at. If you want to actually change the image, you'd probably have to copy each property from imageWithChanges to update.
Another way you can do this, is to attach imageWithChanges to db.Images and say it was a modified instance:
db.Images.Attach((Image)imageWithChanges, true); // true means "it's modified"
db.SaveChanges();
You say you got it fixed, but don't tell How.
For all others that will read this, I agree with Ruben, you have to Attach it. The error it gives you is valid, you have to either handle concurrency checking (with timestamp or version number) or let last in wins (by setting UpdateCheck to false for all your entity's properties).