Error with save! method in Rails 3.1.0-rc6 - sql

Dont know what I am doing wrong...
Have this code:
new_model = Model.new(:brand_id=>brand_id, :name=>new_model_name)
new_model.save!
ModelImage.upload(new_model.id, params[:images])
but new_model.id equals to nil. WTF?
Tried in rails c, no errors. SQl is OK.
Thx.
Some code from Rails Console:
irb(main):045:0> h = Model.create(:brand_id=>2, :name=>'SKyFy')
SQL (0.1ms) BEGIN
SQL (42.2ms) INSERT INTO `models` (`brand_id`, `id`, `name`) VALUES (?, ?, ?) [["brand_id", 2], ["id", nil], ["name", "SKyFy"]]
(118.7ms) COMMIT
=> #<Model id: nil, brand_id: 2, name: "SKyFy">
irb(main):046:0> h.id
=> nil
Dont have any attr_*, validations. Models are clear.
Another example:
irb(main):048:0> h = Model.new(:brand_id=>1, :name=>'SKYDOS')
=> #<Model id: nil, brand_id: 1, name: "SKYDOS">
irb(main):049:0> h.save!
SQL (0.2ms) BEGIN
SQL (3.4ms) INSERT INTO `models` (`brand_id`, `id`, `name`) VALUES (?, ?, ?) [["brand_id", 1], ["id", nil], ["name", "SKYDOS"]]
(83.5ms) COMMIT
=> true
irb(main):050:0> h.id
=> nil
irb(main):051:0> h.errors
=> #<ActiveModel::Errors:0x9a37c18 #base=#<Model id: nil, brand_id: 1, name: "SKYDOS">, #messages={}>
irb(main):052:0>
PS
Solved my problem... had TWO primary keys.
Thx to all.

Set
config.active_record.schema_format = :sql
in config/application.rb
this should hopefully solve your problems.
Note the query,
INSERT INTO `models` (`brand_id`, `id`, `name`) VALUES (?, ?, ?) [["brand_id", 1], ["id", nil], ["name", "SKYDOS"]
This query is wrong, Rails doesn't know about the primary key of the table and assumes (incorrectly) that id is just a normal column. With an SQL schema format - this should work fine.
You can confirm this by looking at db/schema.rb and you will end up with something like:
create_table "foos", :id => false, :force => true do |t|
t.integer "id", :null => false
...
end

It's hard to say with so little information, but the answer most likely lies in your validations for new_model.
Have a look at new_model.rb and see if there are any validations that may be failing.
If not, see if you have an attr_accessible/attr_protected conflict.
To help you find the answer quickly, add the line:
logger.debug new_model.errors
after your save! and you'll see what's going on in your logs.

Make sure that you have primary key 'id' with auto_increment in model's table.

Related

rails - find with condition in rails 4

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')

First active_record insert id is set to NULL automatically

I have this schema:
create_table :tweets, id: false do |t|
# we'll use the id to store the actual tweet ID
t.integer :id, null: false
t.string :text
t.datetime :created_at
t.index :created_at
end
class Tweet < ActiveRecord::Base
end
I'm trying to use active_record, but I will provide my own id values on each insert.
Now when I try to insert the same thing twice, I get an error the first time, but it works after that:
> Tweet.create(id: 123, text: 'foo', created_at: Time.now))
ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR
: null value in column "id" violates not-null constraint
DETAIL: Failing row contains (null, foo, 2014-10-06 15:45:46.149613).
: INSERT INTO "tweets" ("created_at", "text") VALUES ($1, $2)
from /home/mapleoin/tweetwatch/vendor/bundle/ruby/2.0.0/gems/activerecord-4.1.6/l
ib/active_record/connection_adapters/postgresql_adapter.rb:834:in `get_last_result'
> Tweet.create(id: 123, text: 'foo', created_at: Time.now))
=> #<Tweet id: 123, text: "foo", created_at: "2014-10-06 17:45:57">
Using postgresql with the pg gem with active_record 4.1.6
So why is the first insert differetn?

Rails - how to select all records by ID range

I am trying to do a query for all cities (selecting only their name attribute) by their ID, and I want to be able to specify a range of ID's to select. My code is below:
def list_cities(start, stop)
cities = City.all(order: 'name ASC', id: start..stop, select: 'name')
cities.map { |city| "<li> #{city.name} </li>" }.join.html_safe
end
However, I get an error:
Unknown key: id
My implementation in my view is:
<%= list_cities(1,22) %>
This is a helper method to be put in all views, so I am not putting the logic in a particular controller.
My schema for this model is:
create_table "cities", :force => true do |t|
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "neighborhoods"
t.string "name"
t.integer "neighborhood_id"
end
When I ran the method in my console, I got:
City Load (0.9ms) SELECT name FROM "cities" WHERE ("cities"."id" BETWEEN 1 AND 3) ORDER BY name ASC
=> ""
I know it's not an issue of having an empty database since it worked with the following version of the method:
def list_cities(start, stop)
cities = City.all(order: 'name ASC', limit: stop - start, select: 'name')
cities.map { |city| "<li> #{city.name} </li>" }.join.html_safe
end
However, this method returns only the first 'n' records and not a range like I want.
When trying a simpler query in the console:
1.9.3p385 :009 > City.where(:id => 1..4)
City Load (0.9ms) SELECT "cities".* FROM "cities" WHERE ("cities"."id" BETWEEN 1 AND 4)
=> []
I figured out why it was happening...
I did City.all in my console and realized that my cities started with id "946" because I had seeded multiple times and the ID's were not what I thought they were! The solution offered was correct!
City.where(:id => start..stop).order('name ASC').select(:name)
You can turn your query to the following:
cities = City.all(order: 'name ASC', conditions: { id: start..stop }, select: 'name')
Speransky Danil's answer should work perfectly. you can try this too:
City.find((start..stop).to_a,:select=>:name,:order=>'name ASC')

How to get old value of column in a callback?

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
=> {}

Model.find(:all, :conditions ...) on one field for one key-value from a hash?

I have a table of email messages like so:
create_table :emails do |t|
t.string :emailMessageId
t.datetime :date
t.string :subject
t.string :gmailMessageId
t.string :gmailThreadId
t.string :from_hash, :default => nil
t.text :to_hash, :default => nil
t.text :cc_hash, :default => nil
t.integer :contact_id
The email.rb model file says:
class Email < ActiveRecord::Base
serialize :from_hash, Hash
serialize :to_hash, Array
serialize :cc_hash, Array
end
Imagine that
:to_hash = {"name" => "john", "email" => "john#test.com"}
or an array of hashes
:to_hash = [ {"name" => "john", "email" => "john#test.com"}, {"name" => "bob", "email" => "bob#example.com"} ]
As an example, here is Email.first
#<Email id: 1, emailMessageId: "357", date: "2011-10-03 00:39:00", subject: nil,
gmailMessageId: nil, gmailThreadId: nil, from_hash: {"name"=>"melanie",
"email"=>"mel#test.com"}, to_hash: [{"name"=>"michie", "email"=>"mich#blah.com"},
{"name"=>"clarisa", "email"=>"clarisa#123.com"}], cc_hash: [{"name"=>"john",
"email"=>"john#test.com"}, {"name"=>"alex", "email"=>"alex#massimo.com"}], contact_id: 1,
created_at: "2011-10-03 00:39:00", updated_at: "2011-10-03 00:39:00">
Further imagine that my database has thousands of such records, and I want to pull all records keyed on :to_hash["email"]. In other words, I want to be able to find all records in the Email model that contain the email "john#test.com" despite the fact that the email value is within an array of hashes. How do I do this?
I tried variations on:
hash = {"name" => "john", "email" => "john#test.com"}
Email.find(:all, :conditions => ["to_hash = ?", hash]) # returns the following error
ActiveRecord::StatementInvalid: SQLite3::SQLException: near ",": syntax error: SELECT "emails".* FROM "emails" WHERE (to_hash = '---
- name
- john
','---
- email
- john#test.com
')
I also tried:
email = "john#test.com"
Email.find(:all, :conditions => ["to_hash = ?", email])
# => [], which is not an error, but not what I want either!
And finally:
email = "john#test.com"
Email.find(:all, :conditions => ["to_hash['name'] = ?", email])
# which, as expected, gave me a syntax error...
ActiveRecord::StatementInvalid: SQLite3::SQLException: near "['name']": syntax error: SELECT
"emails".* FROM "emails" WHERE (to_hash['name'] = 'john#test.com')
The simple answer is;
if you need to query something, you shouldn't serialize it.
Saying that, I think the answer is just
Email.all(:conditions => ["to_hash LIKE '%email: ?%'", "john#test.com"])
If you look at the database contents this should satisfy you.
But going forward you should look for a better solution.
Serialization is great for storing structured data that you never need to use in a sql query,
but just gets in the way if you do.
If you really need this kind of freeform data structure, I suggest you look at using MongoDB and Mongoid.
However, within the usual Rails world, I'd suggest the following;
class Email
has_many :email_recipients
def to_hash
email_recipients.map do |recipient|
{"name" => recipient.name, "email" => recipient.email}
end
end
end
class EmailRecipient
# with columns
# email_id
# name
# email
belongs_to :email
end
One possible way to do this with just regular Ruby is to use the select method and let ActiveRecord take care of deserialization.
emale = "john#test.com"
Email.find(:all).select { |m| m[:to_hash]["email"] === emale }
Another possible solution is to serialize the search hash and match the serialized hash exactly how it is saved in the database. This requires that the hash has all attributes, not just the e-mail. Some useful links to the code that makes this happen available here. You'll see that ActiveRecord uses YAML for serialization by default, so something like this could work.
search_hash = {"name" => "john", "email" => "john#test.com"}
encoder = ActiveRecord::Coders::YAMLColumn.new(Hash)
search_string = encoder.dump(search_hash)
Email.find(:all, :conditions => ["to_hash = ?", search_string])