So, I have my database, but whenever I call Model.create(:thing => "Hi") it just "does it". When I look at it, my records are All nils! (Minus the ID and Timestamp, those are managed by active record.) Is it the way I create them or IS it my model??? I am using Rails 4.0.1, and it's corresponding active record version. So, what is it? What problems could create this?
My logs:irb(main):003:0>
Email.create(:user => User.find(3), :email => "HIDDEN#HIDDEN.HIDDEN", :key => Email.gen("gemist", "HIDDEN#HIDDEN.HIDDEN"))
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 3]]
WARNING: Can't mass-assign protected attributes for Email: user, email, key
(0.1ms) begin transaction
SQL (2.9ms) INSERT INTO "emails" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Wed, 01 Jan 2014 21:22:24 UTC +00:00], ["updated_at", Wed, 01 Jan 2014 21:22:24 UTC +00:00]]
(1.0ms) commit transaction
=> #<Email id: 3, email: nil, User_id: nil, key: nil, confirmed: nil, created_at: "2014-01-01 21:22:24", updated_at: "2014-01-01 21:22:24">
And my model, incase you are wonder about ZE Generate function.... or anything else
class Email < ActiveRecord::Base
belongs_to :User
def self.gen(user,email)
# Make conf keys on demand
# Salting is used for randominzg and uniquisng, in the case we have already
# sent keys to the same email (We don't want the samekeys more than once!)
# Comboing to make sure that incase of two users who want to confirm the
# same email
salt = BCrypt::Engine.generate_salt
combo = user + email
return BCrypt::Engine.hash_secret(combo, salt)
end
end
I can offer a Migration or schmea if needed.
Now I know under "How to edit" I need to respect the original author, but I have no self respect.
Your question contains the answer already:
WARNING: Can't mass-assign protected attributes for Email: user, email, key
and:
INSERT INTO "emails" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Wed, 01 Jan 2014 21:22:24 UTC +00:00], ["updated_at", Wed, 01 Jan 2014 21:22:24 UTC +00:00]]
You can't set user, email, and key during a mass-assignment (which create() is doing), so it's ignoring those. The only fields being set, as you can see from the INSERT log, are the timestamp fields. So you end up with a fairly empty record in the database.
You can set those fields individually on a model instance, or you can flag them as mass-assignable by putting this in your model:
attr_accessible :user, :email, :key
You may very well want to leave them protected, though. Here's an article on mass-assignment protection. If you're processing form data, you probably want to leave the protection in place for certain fields. If your create() is already using trusted data, you can make them accessible.
Related
I wonder if anyone can help me to understand about the difference between build and create in Has Many Through (HMT) relationship?
From my understanding after reading through stackoverflow and google, create is essentially build + save. However, it seems like create does more than just build + save. It also somehow save into the join table of the HMT relationship as shown below.
I created 3 models: user, wiki, and collaborator.
class User < ActiveRecord::Base
has_many :wikis, through: :collaborators
has_many :collaborators
end
class Wiki < ActiveRecord::Base
has_many :users, through: :collaborators
has_many :collaborators
end
class Collaborator < ActiveRecord::Base
belongs_to :user
belongs_to :wiki
end
Then I tested the build and create behavior in rails console:
$rails c
Loading development environment (Rails 4.0.10)
> user = User.first #create instance user
User Load SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 1, name: "Jayzz55">
> (user.wikis.build(title:"hello",body:"world")).save #build and save wiki
SQL INSERT INTO "wikis" ("body", "created_at", "title", "updated_at") VALUES(?,?,?,?) [["body","world"], ["ccreated_at, Sat, 08 Nov 2014 01:39:36 UTC +00:00], ["title","hello"], ["updated_at",Sat, 08 Nov 2014 01:39:36 UTC +00:00]]
=> true
>wiki1 = Wiki.first #create instance wiki called wiki1
=> #<Wiki id:1, title:"hello",body:"world">
>user.wikis
=> #<Wiki id:1, title:"hello",body:"world">
>user.wikis.count
=> 0
>wiki1.users
=> []
>Collaborator.all
=> []
> user.wikis.create(title:"second title",body:"another one")
begin transaction
SQL INSERT INTO "wikis" ("body", "created_at", "title", "updated_at") VALUES (?, ?, ?, ?)[0m [["body", "another one"], ["created_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00], ["title", "second title"], ["updated_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00]]
SQL INSERT INTO "collaborators" ("created_at", "updated_at", "user_id", "wiki_id") VALUES (?, ?, ?, ?) [["created_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00], ["updated_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00], ["user_id", 1], ["wiki_id", 2]]
=> #<Wiki id:2, title:"second title",body:"another one>
>user.wikis
=> [#<Wiki id: 1, title:"hello",body:"world">, #<Wiki id:2, title:"second title",body:"another one>]
>user.wikis.count
=> 1
>wiki2.users
=>#<User id: 1, name: "Jayzz55">
>Collaborator.all
Collaborator Load SELECT "collaborators".* FROM "collaborators"
=> #<Collaborator id:`, user_id:1, wiki_id: 2>
Case wiki1 : build and then save it
The data is not saved into the join table. calling user.wikis does return the object wiki, but running user.wikis.count return 0. Furthermore, running wiki1.users doesn't return the object user. Checking the join table Collaborator returns empty array.
Case wiki2 : create
The data is saved into the join table. calling user.wikis does return the object wiki. Running user.wikis.count return 1. Furthermore, running wiki1.users does return the object user. Checking the join table Collaborator, it does show the relationship clearly mapped.
Seems like Create is not simply just build + new. I'm curious about this behavior and hopefully someone can share their knowledge on this.
I believe that if you had in your first case instead written:
user.wikis.build(title:"hello",body:"world")
user.save
... you would find that ActiveRecord does the "full" save that create also does. The way you've written it, you're asking ActiveRecord to save the newly created Wiki instance, but not the association. So it doesn't create the record in the join table that's required.
Build + Save is equivalent to Create in any relationship
I have 2 models, Lanzadera and Addict.
Lanzadera has_many :addicts
Addict belongs_to :lanzadera
I have a list of Lanzaderas with a "Sign up" button next to them, that will render the Addict new form.
The user will then fill out a form with the Addict attributes, and that addict should be listed in that particular lanzadera. So, it's all about listing addicts in lanzaderas.
Everything looks great, I can see lanzadera_id in the logs, but when I say #lanzadera.addicts.count it will put 0. That means, addicts are being created but are not being assigned to it's lanzadera.
lanzadera_id is being permitted in addict_params in the controller strong parameters.
My question is, how can I pass lanzadera_id in my form when creating a new Addict, so that the addict gets listed within that Lanzadera?
Started POST "/lanzaderas/1/addicts" for 127.0.0.1 at 2014-07-10 19:25:41 +0200
Processing by AddictsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mJ7pl2xsjhwhdGM2RZBDA+fmD75tpZcsPIaeSwYaBhE=", "addict"=>{"name"=>"Kike", "email"=>"kikeisasi#gmail.com", "city"=>"65", "postal"=>"56478", "street"=>"Aiboa 19, 4", "lanzadera_id"=>""}, "commit"=>"Crear Addict", "lanzadera_id"=>"1"}
(0.2ms) BEGIN
SQL (0.7ms) INSERT INTO "addicts" ("city", "created_at", "email", "name", "postal", "street", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["city", "65"], ["created_at", Thu, 10 Jul 2014 17:25:41 UTC +00:00], ["email", "kikeisasi#gmail.com"], ["name", "Kike"], ["postal", 56478], ["street", "Aiboa 19, 4"], ["updated_at", Thu, 10 Jul 2014 17:25:41 UTC +00:00]]
My guess is that you are missing the landazera_id in the Addicts table in your database. When it begins the INSERT INTO line it doesn't list the id as one of the columns. If you haven't, make sure 'landazera_id' migrated into the table. Rails doesn't do this automatically based on your has_many, belongs_to associations (or at least I've never figured out how to make it automatic) so you'll have to make the migration yourself.
rails g migration add_landazera_id_to_addicts landazera_id:integer
Check the migration in your text editor to make sure it generated correctly. It should include:
def change
add_column :addicts, :landazera_id, :integer
end
Ok, so this may seem like a total newbie ask, but when I create an instance of my model with let, for some reason all of the getter methods on it return nil. Here's my example:
Model:
class Parcel < ActiveRecord::Base
extend UUIDHelper
serialize :address, ActiveRecord::Coders::Hstore
self.rgeo_factory_generator = RGeo::Geographic.spherical_factory srid: 3785
attr_accessible :address, :area_poly, :id, :lonlat, :municipal_id
end
Spec:
require 'spec_helper'
require 'street_address'
describe Parcel do
let(:geo_factory) { RGeo::Geographic.spherical_factory(srid: 3785)}
let(:point1) {geo_factory.point(-72.9229165, 41.3096987)}
let(:point2) {geo_factory.point(-72.9229169, 41.3096987)}
let(:point3) {geo_factory.point(-72.9229169, 41.3096983)}
let(:point4) {geo_factory.point(-72.9229165, 41.3096983)}
let(:line_string) {geo_factory.line_string([point1,point2,point3,point4])}
let(:polygon) {geo_factory.polygon(line_string)}
let(:parcel) { Parcel.create(
municipal_id: 'abc123',
area_poly: polygon,
#lonlat: polygon.centroid,
address: StreetAddress::US.parse('55 Whitney Ave New Haven, CT 06510').as_json
)}
it "#municipal_id should not be nil" do
parcel.municipal_id.nil? should_not be_true
end
end
(the #longlat attribute is commented out because calling the .centroid method on a spherically defined polygon in RGeo is a no-no. Getting the right projection, while on my list of to-dos, is not relevant to this question)
And the spec fails like so:
expected: non-true value
got: #<Parcel id: nil, municipal_id: nil, address: {}, created_at: nil, updated_at: nil, area_poly: nil, lonlat: nil>
FWIW, here's the Rspec log:
Connecting to database specified by database.yml
(0.1ms) BEGIN
(7.2ms) SELECT * FROM geometry_columns WHERE f_table_name='parcels'
(0.1ms) SAVEPOINT active_record_1
SQL (4.2ms) INSERT INTO "parcels" ("address", "area_poly", "created_at", "id", "lonlat", "municipal_id", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) [["address", "\"number\"=>\"55\",\"street\"=>\"Whitney\",\"street_type\"=>\"Ave\",\"unit\"=>NULL,\"unit_prefix\"=>NULL,\"suffix\"=>NULL,\"prefix\"=>NULL,\"city\"=>\"New Haven\",\"state\"=>\"CT\",\"postal_code\"=>\"06510\",\"postal_code_ext\"=>NULL"], ["area_poly", #<RGeo::Geographic::SphericalPolygonImpl:0x80b773e0 "POLYGON ((-72.9229165 41.3096987, -72.9229169 41.3096987, -72.9229169 41.3096983, -72.9229165 41.3096983, -72.9229165 41.3096987))">], ["created_at", Mon, 02 Jun 2014 11:59:51 UTC +00:00], ["id", nil], ["lonlat", nil], ["municipal_id", "abc123"], ["updated_at", Mon, 02 Jun 2014 11:59:51 UTC +00:00]]
(0.1ms) RELEASE SAVEPOINT active_record_1
(0.1ms) ROLLBACK
For reference, I can successfully run Parcel.create in a Rails console, and get instance variables to return non-nil values
I'm running rails 3.2, using postgres 9.3 with postgis 2, and heavily relying on activerecord-postgis-adaptor, RGeo, activerecord-postgres-hstore, among other gems.
Any thoughts?
You're missing a period. should_not is intended to be sent to the object whose value you are checking, as in:
parcel.municipal_id.nil?.should_not be_true
Because it's monkey patched on all objects, though, you were invoking it on RSpec's subject and passing the result (a Proc) as a block to nil?, which is ignoring it. The error message reflects that the object being evaluated is the subject Parcel instance.
As an aside, note that the current and preferred syntax for this in RSpec is:
expect(parcel.municipal_id.nil?).to_not be_truthy # be_true was renamed to be_truthy in RSpec 3
or if you really want to only check for a value of true:
expect(parcel.municipal_id.nil?).to_not be(true)
or if you want more compactness:
expect(parcel.municipal_id).to_not be_nil
or if you prefer the Shakespearean feel:
expect(parcel.municipal_id).to be
or if you want to take advantage of its, which was factored out into a separate gem as of RSpec 3, then you can replace the entire it call with:
its(:municipal_id) { should_not be_nil }
I have two models join by a middle model
class Integration < ActiveRecord::Base
has_many :integration_records
has_many :records, through: :integration_records
end
class IntegrationRecord < ActiveRecord::Base
belongs_to :integration
belongs_to :record
end
class Record < ActiveRecord::Base
has_many :integration_records
has_many :integrations, through: :integration_records
end
i = Integration.create(whatever)
i.records.create(whatever)
=> (0.1ms) BEGIN
SQL (8.7ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:02 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:02 UTC +00:00]]
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
i.records
=> [one record]
Record.all.count
=> 2
i.integration_records
=> #<IntegrationRecord id: nil, integration_id: 1, record_id: 2 >
Notice id is nil
IntegrationRecord.all
=> #<ActiveRecord::Relation []>
IntegrationRecord.create
=> #<IntegrationRecord id: nil, integration_id: nil, record_id: nil>
Notice id is nil
Record.create.integrations.create
=> (0.1ms) BEGIN
SQL (8.7ms) INSERT INTO "integrations" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:02 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:02 UTC +00:00]]
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
Not sure why this is happening, in the case of i.records.create(whatever) it should output:
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
INSERT INTO "integration_records" ("created_at", "data", "integration_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Sat, 03 May 2014 15:57:05 UTC +00:00], ["data", {}], ["integration_id", 5], ["updated_at", Sat, 03 May 2014 15:57:05 UTC +00:00]]
I should note that for some reason, when I create a new app in rails 4.0.4, I still had this problem. But when I change the names of the models and specifically Record model, it works perfectly fine. So when I changed it to Recordrow no problem at all.
As per your current association setup, all you need to do is, just follow these simple instructions step by step
Create an Integration record
## this will create an "integration" record in "integrations" table
integration = Integration.create(whatever)
Create an associated Record record
## this will create an associated "record" entry in "records" table
## PLUS this will also created an associated record in "integration_records" table
integration.records.create(whatever)
View the associated records and associated integration_records
## Display all the "records" entries associated to this particular integration
integration.records
## Display all the "integration_records" entries associated to this particular integration
integration.integration_records
UPDATE
i.records.create(whatever) was creating 2 records, found out that the issue was with the name records of the table. Once changed everything works fine. It looks like records is reserved word.
Also, OP found this link which states records is reserved
I have an ActiveRecord object that has a before_create hook that generates a SHA hash and stores the result in an attribute of the object:
before_create :store_api_id
attr_reader :api_id
def store_api_id
self.api_id = generate_api_id
end
private
def generate_api_id
Digest::SHA1.hexdigest([Time.now.nsec, rand].join).encode('UTF-8')
end
This works in that the api_id attribute is created and stored in the database as text (which is forced by the call to .encode('UTF-8') otherwise SQLite will try to store the result as binary data.
However the following specs are failing:
it "should be available as ad.api_id" do
#ad.save!
#ad.api_id.should_not be_nil
end
it "should match the directly accessed attribute" do
#ad.save!
#ad.api_id.should == #ad.attributes['api_id']
end
I can get the correct hash by using ad.api_id_before_type_cast and ad.attributes['api_id'] but not when using ad.api_id.
Ad.find_by_api_id('string api id') also works as expected, but still returns null when calling .api_id on the returned object.
I have double-checked the type in the database as follows:
sqlite> select typeof(api_id) from ads;
text
Here is an example rails console session on a fresh Rails 3.2.2 / ruby 1.9.3-p125 app:
Loading development environment (Rails 3.2.2)
irb(main):001:0> ex = Example.new
=> #<Example id: nil, api_id: nil, created_at: nil, updated_at: nil>
irb(main):002:0> ex.save! (0.1ms) begin transaction
SQL (7.3ms) INSERT INTO "examples" ("api_id", "created_at", "updated_at")
VALUES (?, ?, ?) [["api_id", "fc83bb94420cf8fb689b9b33195318778d771c4e"],
["created_at", Fri, 23 Mar 2012 10:17:24 UTC +00:00],
["updated_at", Fri, 23 Mar 2012 10:17:24 UTC +00:00]]
(1.0ms) commit transaction
=> true
irb(main):003:0> ex.api_id
=> nil
irb(main):004:0> ex.api_id_before_type_cast
=> "fc83bb94420cf8fb689b9b33195318778d771c4e"
irb(main):005:0> ex.attributes['api_id']
=> "fc83bb94420cf8fb689b9b33195318778d771c4e"
irb(main):006:0>
As I wrote above, using attr_readonly instead of attr_reader to protect the attribute fixed this issue for me, and in this case is actually closer to what I want.
As the API docs note:
Attributes listed as readonly will be used to create a new record but update operations will ignore these fields.