to_sql not working on update_attributes or .save
These methods both return a boolean. You can't invoke to_sql
on a boolean.
Rails: save doesn't update record if called twice?
Rails Models are 'Dirty' by default, meaning that attribute setting functions, attribute=()
, mark the attribute as changed and tell Rails to update this attribute on the next save. This info is stored on the model, NOT in the database. On var.save
, Rails only updates attributes it knows to have changed. Rails does not check the database for a discrepancy if it thinks nothing has changed (this would be incredibly slow in a production environment).
You can use var.reload
to reload attributes from the database into the corresponding object.
EDIT: To clarify the comments being made above, you should be using MyModel.last
not MyModel.first
in your test code. If you have more than one row in your database, MyModel.first
will not refer to the most recently saved object, and therefore var2
and var
will refer to completely different objects.
How to update attributes without validation
USE update_attribute instead of update_attributes
Updates a single attribute and saves the record without going through the normal validation procedure.
if a.update_attribute('state', a.state)
Note:- 'update_attribute' update only one attribute at a time from the code given in question i think it will work for you.
Django: save() vs update() to update the database?
There are several key differences.
update
is used on a queryset, so it is possible to update multiple objects at once.
As @FallenAngel pointed out, there are differences in how custom save()
method triggers, but it is also important to keep in mind signals
and ModelManagers
. I have build a small testing app to show some valuable differencies. I am using Python 2.7.5, Django==1.7.7 and SQLite, note that the final SQLs may vary on different versions of Django and different database engines.
Ok, here's the example code.
models.py
:
from __future__ import print_function
from django.db import models
from django.db.models import signals
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
__author__ = 'sobolevn'
class CustomManager(models.Manager):
def get_queryset(self):
super_query = super(models.Manager, self).get_queryset()
print('Manager is called', super_query)
return super_query
class ExtraObject(models.Model):
name = models.CharField(max_length=30)
def __unicode__(self):
return self.name
class TestModel(models.Model):
name = models.CharField(max_length=30)
key = models.ForeignKey('ExtraObject')
many = models.ManyToManyField('ExtraObject', related_name='extras')
objects = CustomManager()
def save(self, *args, **kwargs):
print('save() is called.')
super(TestModel, self).save(*args, **kwargs)
def __unicode__(self):
# Never do such things (access by foreing key) in real life,
# because it hits the database.
return u'{} {} {}'.format(self.name, self.key.name, self.many.count())
@receiver(pre_save, sender=TestModel)
@receiver(post_save, sender=TestModel)
def reicever(*args, **kwargs):
print('signal dispatched')
views.py
:
def index(request):
if request and request.method == 'GET':
from models import ExtraObject, TestModel
# Create exmple data if table is empty:
if TestModel.objects.count() == 0:
for i in range(15):
extra = ExtraObject.objects.create(name=str(i))
test = TestModel.objects.create(key=extra, name='test_%d' % i)
test.many.add(test)
print test
to_edit = TestModel.objects.get(id=1)
to_edit.name = 'edited_test'
to_edit.key = ExtraObject.objects.create(name='new_for')
to_edit.save()
new_key = ExtraObject.objects.create(name='new_for_update')
to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key)
# return any kind of HttpResponse
That resuled in these SQL queries:
# to_edit = TestModel.objects.get(id=1):
QUERY = u'SELECT "main_testmodel"."id", "main_testmodel"."name", "main_testmodel"."key_id"
FROM "main_testmodel"
WHERE "main_testmodel"."id" = %s LIMIT 21'
- PARAMS = (u'1',)
# to_edit.save():
QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s
WHERE "main_testmodel"."id" = %s'
- PARAMS = (u"'edited_test'", u'2', u'1')
# to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key):
QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s
WHERE "main_testmodel"."id" = %s'
- PARAMS = (u"'updated_name'", u'3', u'2')
We have just one query for update()
and two for save()
.
Next, lets talk about overriding save()
method. It is called only once for save()
method obviously. It is worth mentioning, that .objects.create()
also calls save()
method.
But update()
does not call save()
on models. And if no save()
method is called for update()
, so the signals are not triggered either. Output:
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
# TestModel.objects.get(id=1):
Manager is called [<TestModel: edited_test new_for 0>]
Manager is called [<TestModel: edited_test new_for 0>]
save() is called.
signal dispatched
signal dispatched
# to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key):
Manager is called [<TestModel: edited_test new_for 0>]
As you can see save()
triggers Manager
's get_queryset()
twice. When update()
only once.
Resolution. If you need to "silently" update your values, without save()
been called - use update
. Usecases: last_seen
user's field. When you need to update your model properly use save()
.
Rails 4: .save does not update updated_at for existing records?
As you can see in your log, there is no UPDATE
SQL query that is executed. Rails is not updating your record at all. This is because .save
actually saves the record only if changes were made.
There is the method .touch
(Documentation) that you can call in order to update the updated_at
field without having to do changes to your record:
1.9.3p489 :005 > Intervention.first.touch
Intervention Load (12.9ms) SELECT "interventions".* FROM "interventions" LIMIT 1
SQL (20.5ms) UPDATE "interventions" SET "updated_at" = '2014-06-18 16:34:03.924648' WHERE "interventions"."id" = 1
=> true
Here we see the UPDATE
SQL query.
How to update value of a Model's attribute
here is the solution
item = Item.find(1)
item.popularity = 1
item.save
When do I use save!, create! and update_attributes! in Rails?
Generally you want to use the non-bang versions in your controllers. This allows logic like this:
def update
@model = Model.find params[:id]
if @model.update_attributes params[:model] #returns true of false
# handle success
else
# handle failure
end
end
I find myself using the bang versions a lot in tests when I want to make sure I know if something doesn't validate, and isn't saved. I've definitely wasted time debugging tests that were failing because of changed model validations, which would be obvious if I used the bang versions.
e.g.
it "should do something" do
m = Model.create! :foo => 'bar' # will raise an error on validation failure
m.should do_something
end
In terms of not having invalid data in the database, you should be handling this with ActiveRecord validations (e.g. validates_presence_of :user_id
), or defining your own validate
method in the model. (http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html) This should prevent saves from occurring if your data isn't valid. If you're really paranoid you can add some constraints to your database. Check the ActiveRecord::Migration
docs for how to set up unique indexes and other database constraints in your migrations.
Also in my experience you want to avoid using any custom save or create method whenever possible. If you re-implement functionality included in ActiveRecord you end up paying a price down the road. http://matthewpaulmoore.com/post/5190436725/ruby-on-rails-code-quality-checklist has more to say on this.
django save() refuses to update an existing record
I would advise to update the querset in bulk, with .update(…)
[Django-doc]:
from django.db.models import F
@database_sync_to_async
def increment_unread(room_id):
RoomOccupier.objects.filter(room_id=room_id).update(
unread_count=F('unread_count')+1
)
return True
id attribute not updating in sqlite
Rails won't let you update the id
attribute easily. You could execute the following SQL though, and it should work. (Also, when you use the update_attributes(...)
method, you don't need to call .save
afterward).
sql = "update people set id=5 where id=4"
ActiveRecord::Base.connection.execute(sql)
Related Topics
Error Loading the 'Sqlite3' Active Record Adapter. When I Deploy in Heroku
Create a Titleize Method, That Excludes "Little Words"
Can't Run a Ruby Hello World Application in Aptana
How to Create Nested Model from Partial Rails 6
Unexpected =>, Expecting '}' in Rspec Expect
Twitter API - Ruby Twitter Gem
Devise Password Reset from Rails Console
Is It a Bad Idea Do Divide the Models into Directories
How to Ignore Irrelevant Methods When Profiling Ruby Applications
How to Return a Group of Sequential Numbers That Might Exist in an Array
Why Won't Ternary Operator Work with Redirect
Compass CSS Framework - Using Bootstrap with SASS
Ruby, No Implicit Conversion of Symbol into Integer
API Post with Array Using Http Gem (Or Restclient)