On after_update, in my model. How can I get the old value of a column?
changed_attributes() method of your model will get you a hash of changed attributes with their original values, even after_udpate. More info and more related methods here.
class MyModel < ActiveRecord::Base
after_update :log_changed
def log_changed
puts "changed attributes:"
puts changed_attributes.inspect
end
end
... gives the following in the console:
$ rails console
Loading development environment (Rails 3.0.7)
test(dev)> m = MyModel.first
=> #<MyModel id: 134611365, name: "oldname", created_at: "2011-09-16 10:27:53", updated_at: "2011-09-20 11:58:11">
test(dev)> m.name = 'newname'
=> "newname"
test(dev)> m.save
SQL (0.2ms) BEGIN
SQL (0.4ms) SHOW TABLES
AREL (0.2ms) UPDATE `mymodels` SET `updated_at` = '2011-09-20 12:07:34', `name` = 'newname' WHERE `mymodels`.`id` = 134611365
changed attributes:
{"name"=>"oldname", "updated_at"=>Tue, 20 Sep 2011 11:58:11 UTC +00:00}
SQL (83.9ms) COMMIT
=> true
test(dev)> m.changed_attributes
=> {}
Related
I am confused by Ruby's ActiveRecord uniq method. I am using it to try to get back an array of complete objects, not just a particular field.
In my Padrino app script, which saves newspaper names and scores as Score objects, the uniq method by attribute on an ActiveRecord Relation is not working, and neither is distinct, with or without SQL syntax. Can anyone explain what is going on?
class Score < ActiveRecord::Base
def self.from_today
self.where('created_at > ?', Date.today)
end
end
scores = Score.from_today
scores.class
=> Score::ActiveRecord_Relation
scores.first
=> #<Score id: 123, score: -2.55, source: "Mail", created_at: "2016-08-11 04:29:24", updated_at: "2016-08-11 04:29:24">
scores.map(&:source)
=> ["Mail", "Guardian", "Telegraph", "Independent", "Express", "Mail"]
scores.uniq(:source).count
=> 6
scores.distinct(:source).count
=> 6
scores.select('distinct (SOURCE)').count
=> 5 #AHA, it works!
scores.select(:source).distinct
=> #<ActiveRecord::Relation [#<Score id: nil, source: "Telegraph">, #<Score id: nil, source: "Mail">, #<Score id: nil, source: "Independent">, #<Score id: nil, source: "Express">, #<Score id: nil, source: "Guardian">]>
#oops, no it doesn't
In Rails 5 distinct has no parameter. In Rails 4.2 the parameter is only true or false. When true, return distinct records, when false return none distinct records. uniq is in this case only an alias for distinct
So for Rails 4
scores.select(:source).distinct.count is what you want. This restricts distinct to column source
I recently upgraded my rails to Rails 4.1.6.
This query used to work :
#user = User.find(:all, :conditions => { :name => 'batman' })
Now I get this error message:
Couldn't find all Users with 'id': (all, {:conditions=>{:name=>"batman"}}) (found 0 results, but was looking for 2)
When I check the logs I can see that rails is trying to do a completely different query :
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ('all', '---
:conditions:
:name: batman
')
It looks like, it's trying to get all the users with the id "all" and "{:conditions=>{:name=>"batman"}}". Please help.
UPDATE:
My real question behind that was I want to get a specific user and add to it his cars, only the cars that are blue. For example this is my query, the user id is 20.
#user = User.joins(:cars).find(20, :cars => {:color => "blue"})
But I get this error:
Couldn't find all Users with 'id': (20, {:cars=>{:color=>"blue"}})
(found 41 results, but was looking for 2)
You should definitely read this ActiveRecord Query Interface quide
User.where(name: "batman")
Some others already pointed out: The query syntax changed. Try this:
#user = User.joins(:cars).where(:cars => { :color => "blue" }).find(20)
Note that this will raise an exception if that record is not found, to return an array empty instead call:
#user = User.joins(:cars).where(:id => 20, :cars => { :color => "blue" })
I suggest to read: http://guides.rubyonrails.org/active_record_querying.html
If you want to load the user even if he does not have any cars and than display only his blue cars, I would do it like this:
#user = User.find(20) # returns the user
#user.cars.where(:color => 'blue') # returns the user's blue cars (or an empty array)
The find method is deprecated in this version of Rails (see the reference).
Instead, you must use the where method.
In your case, you should write #user = User(:name => 'batman') or #user = User(name: 'batman')
Why doesn't this work?
class Foo
...
Status.each do |status|
scope status, where(status: status)
end
...
end
Now Foo.new it returns not an instance of Foo but an ActiveRecord::Relation.
Try this in ruby 1.9
Status.each do |status|
scope status, -> { where(status: status) }
end
or in previous ruby versions
Status.each do |status|
scope status, lambda { where(status: status) }
end
-- EDIT --
I guess your problem is somewhere else, since this code is working for me:
class Agency < ActiveRecord::Base
attr_accessible :logo, :name
validate :name, presence: true, uniqueness: true
NAMES = %w(john matt david)
NAMES.each do |name|
scope name, -> { where(name: name) }
end
end
I can create new models just fine and use the scopes
irb(main):003:0> Agency.new
=> #<Agency id: nil, name: nil, logo: nil, created_at: nil, updated_at: nil>
irb(main):004:0> Agency.matt
Agency Load (0.5ms) SELECT "agencies".* FROM "agencies" WHERE "agencies"."name" = 'matt'
=> []
irb(main):005:0> Agency.john
Agency Load (0.3ms) SELECT "agencies".* FROM "agencies" WHERE "agencies"."name" = 'john'
=> []
irb(main):006:0> Agency.david
Agency Load (0.3ms) SELECT "agencies".* FROM "agencies" WHERE "agencies"."name" = 'david'
=> []
I have created a table that implements an n-to-n relation using the following statement:
create_table :capabilities_roles, :id => false do |t|
t.integer :capability_id, :null => false
t.integer :role_id, :null => false
end
There is no model for this table. How do I insert records without resorting to SQL?
I found this in the ActiveRecord::ConnectionAdapters::DatabaseStatements module:
insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
and also:
insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
I have no idea what arel means. Can someone give me an example of a valid insert? I would like to use stuff like :role_id => Role.find_by_name('Business user') in it.
If you're going to be manipulating the database records via Rails, then there should be a model for it. Just create a role.rb in your models directory with the lines
class Role < ActiveRecord::Base
end
And you're as good as gold.
It looks like a join table for has and belongs to many relationship between Capability and Role models. You should let the Rails handle it for you. First define required associations:
class Capability < ActiveRecord::Base
has_and_belongs_to_many :roles
end
class Role < ActiveRecord::Base
has_and_belongs_to_many :capabilities
end
Then just add instance of Role model to roles array of an instance of Capability model (or vice versa):
capability.roles << role
role.capabilities << capability
Removing records from join table is done via removing object from an array:
capability.roles -= [role]
In our project we have many meta tables which don't have models. To generate active record models on the fly we use follow module:
module EntityModel
module_function
ACCESS_MODELS = {}
def for(table_name)
return ACCESS_MODELS[table_name] if ACCESS_MODELS.has_key?(table_name.id)
ACCESS_MODELS[table_name] = create_access_model(table_name)
end
def create_access_model(table_name)
Class.new(ActiveRecord::Base) do
self.table_name = table_name
end
end
end
This create anonymous models and store it in the global hash for performance purposes.
Uses as:
EntityModel.for(:users)
For some reason my db:seed is not working when I try to do it via heroku. It works fine when I do it locally.
paul#paul-laptop:~/rails_projects/foglift$ heroku rake db:seed
rake aborted!
PGError: ERROR: null value in column "id" violates not-null constraint
: INSERT INTO "metric_records" ("metric_id", "id", "created_at", "updated_at", "value", "school_id", "date") VALUES (1, NULL, '2011-03-18 20:26:39.792164', '2011-03-18 20:26:39.792164', 463866000.0, 1, '2009-01-01') RETURNING "id"
(See full trace by running task with --trace)
(in /app/5aca24ae-c5a7-4805-a3a1-b2632a5cf558/home)
I'm not sure why it's attempting to put a null into the id column, it doesn't make any sense to me.
Here's my seeds.rb file.
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
directory = "db/seed_data/"
# Pre-load All Schools
School.delete_all
path = File.join(directory, "schools.txt")
open(path) do |schools|
schools.read.each_line do |school|
name, city, state, website, tuition, debt = school.chomp.split("|")
School.create!(:name => name, :city => city, :state => state, :website => website, :current_tuition => tuition, :current_avg_debt => debt)
end
end
# Pre-load All Dashboards
Dashboard.delete_all
path = File.join(directory, "dashboards.txt")
open(path) do |dashboards|
dashboards.read.each_line do |dashboard|
name, extra = dashboard.chomp.split("|")
Dashboard.create!(:name => name)
end
end
# Pre-load All Metrics
Metric.delete_all
path = File.join(directory, "metrics.txt")
open(path) do |metrics|
metrics.read.each_line do |metric|
name, dashboard_id, display, source = metric.chomp.split("|")
Metric.create!(:name => name, :dashboard_id => dashboard_id, :display => display, :source => source)
end
end
# Pre-load All Metric Records
MetricRecord.delete_all
path = File.join(directory, "metric_records.txt")
open(path) do |values|
values.read.each_line do |value|
metric_id, value, date_string, school_id = value.chomp.split("|")
date_string = '01/01/' << date_string
date = Date.strptime(date_string, "%d/%m/%Y") unless date_string.nil?
MetricRecord.create!(:metric_id => metric_id, :value => value, :date => date, :school_id => school_id)
end
end
Is there a possibility that your DB migration did not create a sequence for your metric_records ID column on the Heroku database?
You might be able to fix it with the following:
CREATE SEQUENCE metric_records_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE metric_records_id_seq OWNED BY metric_records.id;