I apologize for asking such a noob question, but I'm really confused why this isn't working. I'm creating a reddit clone as a way to teach myself rails. When a new Post is created, I would like to set the upvote and downvote attributes to zero. What am I doing wrong?
EDIT: It looks like before_save works when I create a new Post through my rails server, but it does not work when creating a new Post in the console. Why is this?
post.rb
class Post < ActiveRecord::Base
attr_accessible :title, :url, :upvotes, :downvotes
before_save :set_votes_to_zero
def set_votes_to_zero
self.upvotes = 0
self.downvotes = 0
end
end
schema.rb
ActiveRecord::Schema.define(:version => 20121223192629) do
create_table "posts", :force => true do |t|
t.string "title"
t.string "url"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "upvotes"
t.integer "downvotes"
end
end
Rails console output when creating a new post
irb(main):035:0> Post.create(title: "Facebook", url: "www.facebook.com")
←[1m←[35m (0.0ms)←[0m begin transaction
←[1m←[36mSQL (0.0ms)←[0m ←[1mINSERT INTO "posts" ("created_at", "downvotes",
"title", "updated_at", "upvotes", "url") VALUES (?, ?, ?, ?, ?, ?)←[0m [["creat
ed_at", Sun, 23 Dec 2012 19:43:37 UTC +00:00], ["downvotes", nil], ["title", "Fa
cebook"], ["updated_at", Sun, 23 Dec 2012 19:43:37 UTC +00:00], ["upvotes", nil]
, ["url", "www.facebook.com"]]
←[1m←[35m (124.8ms)←[0m commit transaction
=> #<Post id: 9, title: "Facebook", url: "www.facebook.com", created_at: "2012-1
2-23 19:43:37", updated_at: "2012-12-23 19:43:37", upvotes: nil, downvotes: nil>
Related
I can not for the life of me figure out why certain attributes of my paperclip attachment aren't not getting saved to the database via nested attributes. My post has one attachment but the :media params for the :attachment attribute not being saved, but the attachment object is being created. I figure I have something messed up with the way my strong params are being passed in. Thanks in advance.
NIL Params
media_file_name: nil, media_content_type: nil, media_file_size: nil, media_updated_at: nil>
IRB:
irb(main):001:0> p = Post.last
Post Load (0.1ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT 1
=> #<Post id: 40, body: "test info", expiration: 1467065130586, created_at: "2016-06-27 21:05:41", updated_at: "2016-06-27 21:05:41">
irb(main):002:0> p.attachment
Attachment Load (0.2ms) SELECT "attachments".* FROM "attachments" WHERE "attachments"."attachable_id" = ? AND "attachments"."attachable_type" = ? LIMIT 1 [["attachable_id", 40], ["attachable_type", "Post"]]
=> #<Attachment id: 26, attachable_id: 40, attachable_type: "Post", created_at: "2016-06-27 21:05:41", updated_at: "2016-06-27 21:05:41", media_file_name: nil, media_content_type: nil, media_file_size: nil, media_updated_at: nil>
irb(main):003:0>
Schema:
ActiveRecord::Schema.define(version: 20160627165514) do
create_table "attachments", force: :cascade do |t|
t.integer "attachable_id"
t.string "attachable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "media_file_name"
t.string "media_content_type"
t.integer "media_file_size"
t.datetime "media_updated_at"
end
add_index "attachments", ["attachable_type", "attachable_id"], name: "index_attachments_on_attachable_type_and_attachable_id"
create_table "posts", force: :cascade do |t|
t.text "body"
t.integer "expiration", limit: 8
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Post Model:
class Post < ActiveRecord::Base
has_one :attachment, as: :attachable, dependent: :destroy
accepts_nested_attributes_for :attachment
end
Post Controller:
class PostsController < ApplicationController
def show
#I don't need this.
end
def index
#posts = Post.all
end
def new
#post = Post.new
#post.build_attachment
end
def create
#post = Post.new(post_params)
#post.build_attachment
if #post.save
respond_to do |format|
format.html {redirect_to(posts_path, notice:"Post succesfully created")}
format.js
end
else
render :new
end
end
private
def post_params
params.require(:post).permit(:body,:expiration, attachment_attributes: [:media])
end
end
Attachment Model:
class Attachment < ActiveRecord::Base
belongs_to :attachable, polymorphic:true
has_attached_file :media
end
HTML FORM:
<%=form_for(#post, url: posts_path, html:{multipart:true}) do |form|%>
<%=form.text_area :body%>
<%=fields_for(:attachment) do |ff|%>
<%= ff.file_field :media %>
<%end%>
<%=form.hidden_field :expiration%>
<%=form.submit "Post", class: "post-submit-button"%>
<%end%>
Log Dumb:
Started POST "/posts" for ::1 at 2016-06-27 16:05:41 -0500
Processing by PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"/b54OJOBJIaR551MAHDujCxjciI7OAx93EQAVrugCz0xtYnafaSH4widYOftZJUbDHLfmI6jly5eG5AxCCNJbQ==", "post"=>{"body"=>"test info", "expiration"=>"1467065130586"}, "attachment"=>{"media"=>"IMG_1004.jpg"}, "commit"=>"Post"}
(0.1ms) begin transaction
SQL (0.3ms) INSERT INTO "posts" ("body", "expiration", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["body", "test info"], ["expiration", 1467065130586], ["created_at", "2016-06-27 21:05:41.321484"], ["updated_at", "2016-06-27 21:05:41.321484"]]
SQL (0.1ms) INSERT INTO "attachments" ("attachable_type", "attachable_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["attachable_type", "Post"], ["attachable_id", 40], ["created_at", "2016-06-27 21:05:41.323335"], ["updated_at", "2016-06-27 21:05:41.323335"]]
(1.4ms) commit transaction
Redirected to http://localhost:3000/posts
Completed 302 Found in 8ms (ActiveRecord: 1.9ms)
Started GET "/posts" for ::1 at 2016-06-27 16:05:41 -0500
Processing by PostsController#index as HTML
Rendered layouts/_user_menu.html.erb (0.1ms)
Rendered posts/index.html.erb within layouts/application (1.8ms)
Completed 200 OK in 26ms (Views: 25.8ms | ActiveRecord: 0.0ms)
You're not binding your fields_for to your form. Try this:
<%=form_for(#post, url: posts_path, html:{multipart:true}) do |form|%>
<%=form.text_area :body%>
<%= form.fields_for(:attachment) do |ff|%>
<%= ff.file_field :media %>
<% end %>
<%=form.hidden_field :expiration%>
<%=form.submit "Post", class: "post-submit-button"%>
<%end%>
I'm learning ruby on rails and this is my first question in SO. I'm making a DB to manage products. In part of my squema I have users that can have many brands, and brands that can have many users. So my squema looks like this:
class Brand < ActiveRecord::Base
has_and_belongs_to_many :users
.
.
.
end
class User < ActiveRecord::Base
has_and_belongs_to_many :brands
.
.
.
end
ActiveRecord::Schema.define(version: 20160614163029) do
create_table "brands", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "brands_users", id: false, force: :cascade do |t|
t.integer "user_id"
t.integer "brand_id"
end
add_index "brands_users", ["brand_id"], name: "index_brands_users_on_brand_id"
add_index "brands_users", ["user_id"], name: "index_brands_users_on_user_id"
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
end
So when I create and user it has to put the name of his brand so I can create a brand associated to that user.
user.brands.create(name: "Boo")
This creates the association of the user with the brand so I can call user.brands and it gives me "Boo". My problem is that when I call:
brand.users
I get
#<ActiveRecord::Associations::CollectionProxy []>
I thought the association of this two objects should be automatic I have been searching but I don't know what I'm missing. Do I need to make manually the association from that brand to the user?
Thanks
Fixed. Reloading console fixed the issue. And now it works like I wanted:
2.3.1 :001 > user = User.second
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 OFFSET 1
=> #<User id: 2, name: "Luis Hirthe DVM", email: "example-1#example.org", created_at: "2016-06-14 16:52:17", updated_at: "2016-06-14 16:52:17">
2.3.1 :002 > user.brands.create(name: "Foo")
(0.1ms) SAVEPOINT active_record_1
SQL (0.5ms) INSERT INTO "brands" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "Foo"], ["created_at", "2016-06-14 20:23:08.410200"], ["updated_at", "2016-06-14 20:23:08.410200"]]
SQL (0.2ms) INSERT INTO "brands_users" ("user_id", "brand_id") VALUES (?, ?) [["user_id", 2], ["brand_id", 1]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> #<Brand id: 1, name: "Foo", created_at: "2016-06-14 20:23:08", updated_at: "2016-06-14 20:23:08">
2.3.1 :003 > brand = Brand.first
Brand Load (0.3ms) SELECT "brands".* FROM "brands" ORDER BY "brands"."id" ASC LIMIT 1
=> #<Brand id: 1, name: "Foo", created_at: "2016-06-14 20:23:08", updated_at: "2016-06-14 20:23:08">
2.3.1 :004 > brand.users.all
User Load (0.2ms) SELECT "users".* FROM "users" INNER JOIN "brands_users" ON "users"."id" = "brands_users"."user_id" WHERE "brands_users"."brand_id" = ? [["brand_id", 1]]
=> #<ActiveRecord::AssociationRelation [#<User id: 2, name: "Luis Hirthe DVM", email: "example-1#example.org", created_at: "2016-06-14 16:52:17", updated_at: "2016-06-14 16:52:17">
I hope this relationship example help other beginners.
I followed this tutorial to add a many to many association:
https://www.youtube.com/watch?v=ZJXHmesqJr0&index=35&list=PLvG9ngQqIEISob71DI7Id68lXLQ4hVoVH
My goal is to create a products table, a choices table and a connection between these tables (many to many). On those connections I need to store information like price.
This is my code
Schema.rb:
ActiveRecord::Schema.define(version: 20141213005617) do
create_table "choice_product_links", force: true do |t|
t.integer "price"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "choice_id"
t.integer "product_id"
end
create_table "choices", force: true do |t|
t.string "name"
t.decimal "price"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "products", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Models are:
class Choice < ActiveRecord::Base
has_many :choice_product_links
has_many :products, :through => :choice_product_links
end
class ChoiceProductLink < ActiveRecord::Base
belongs_to :product
belongs_to :choice
end
class Product < ActiveRecord::Base
has_many :choice_product_links
has_many :choices, :through => :choice_product_links
end
When I use the console to test my code, I enter a product p1 (works fine), a choice c1 (works fine). The problem occurs when I do the following:
>> cpl1 = ChoiceProductLink.new
#<ChoiceProductLink id: nil, price: nil, created_at: nil, updated_at: nil, choice_id: nil, product_id: nil>
>> cpl1.save
true
>> cpl1.product = p1
NoMethodError: undefined method `product=' for #<ChoiceProductLink:0x00000101f31bf8>
Can somebody help me?
Thanks guys
My previous answer was wrong -- sorry. I'm not entirely sure why your model isn't working properly. I just set up a test Rails application and ran all of the code you ran with the same migrations and class structure, and got these results:
Joshuas-MacBook-Pro:models joshua$ rails console
Loading development environment (Rails 4.0.0)
irb(main):001:0> cpl1 = ChoiceProductLink.new
=> #<ChoiceProductLink id: nil, price: nil, created_at: nil, updated_at: nil, choice_id: nil, product_id: nil>
irb(main):002:0> cpl1.save
(0.1ms) begin transaction
SQL (7.0ms) INSERT INTO "choice_product_links" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Sat, 13 Dec 2014 03:12:50 UTC +00:00], ["updated_at", Sat, 13 Dec 2014 03:12:50 UTC +00:00]]
(0.7ms) commit transaction
=> true
irb(main):004:0* p1 = Product.new
=> #<Product id: nil, name: nil, created_at: nil, updated_at: nil>
irb(main):005:0> p1.save
(0.1ms) begin transaction
SQL (1.5ms) INSERT INTO "products" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Sat, 13 Dec 2014 03:13:25 UTC +00:00], ["updated_at", Sat, 13 Dec 2014 03:13:25 UTC +00:00]]
(2.5ms) commit transaction
=> true
irb(main):006:0> cpl1.product = p1
=> #<Product id: 1, name: nil, created_at: "2014-12-13 03:13:25", updated_at: "2014-12-13 03:13:25">
irb(main):007:0> cpl1.save
(0.1ms) begin transaction
SQL (0.7ms) UPDATE "choice_product_links" SET "product_id" = ?, "updated_at" = ? WHERE "choice_product_links"."id" = 1 [["product_id", 1], ["updated_at", Sat, 13 Dec 2014 03:13:35 UTC +00:00]]
(1.6ms) commit transaction
=> true
Is this similar to what you did? As far as I can tell it works...
The solution is that I create a migration file that contains the following code:
class CreateConstraints2 < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.timestamps
end
create_table :choices do |t|
t.string :name
t.timestamps
end
create_table : choice_product_links do |t|
t.belongs_to :product
t.belongs_to :choice
t.datetime : choice_product_links
t.timestamps
end
end
end
With that migration file everything works perfectly. What I did before was that I created the tables with separate migration files and added the "has_many" and "belongs_to" later.
I dont know why thats different, but that's the solution. I tried almost 100 times back and forth :p
I have a fairly simple has many / belongs to relationship model: Reports have many Records have many Related Publishers. The belongs to objects have a foreign key to ID their parent.
However, I cannot save any 'Related Publishers'. Or rather I 'can', where the commit transaction returns true, but the Related Record is not created and the corresponding SQLite3 command does not make sense.
Why is the SQLite action generated by Rails the command to create an empty Record? Why is the save of the Related Publishers circumvented?
In rails console, with some output truncated
> #report = Report.new => #<Report id: nil, ... >
> #record = #report.records.build(leid: 1234567890) => #<Record id: nil, leid: 1234567890, ...>
> #related_publisher = #record.related_publishers.build(sid: 9876)
=> #<RelatedPublisher id: nil, ..., sid: 9876, ...>
> #report.save
(0.1ms) begin transaction
SQL (4.5ms) INSERT INTO "reports" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Wed, 07 May 2014 11:20:50 UTC +00:00], ["updated_at", Wed, 07 May 2014 11:20:50 UTC +00:00]]
SQL (0.2ms) INSERT INTO "records" ("created_at", "leid", "report_id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Wed, 07 May 2014 11:20:50 UTC +00:00], ["leid", 1234567890], ["report_id", 22], ["updated_at", Wed, 07 May 2014 11:20:50 UTC +00:00]]
SQL (0.3ms) INSERT INTO "records" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Wed, 07 May 2014 11:20:50 UTC +00:00], ["updated_at", Wed, 07 May 2014 11:20:50 UTC +00:00]]
(0.7ms) commit transaction
=> true
> RelatedPublisher.all
RelatedPublisher Load (0.3ms) SELECT "related_publishers".* FROM "related_publishers"
=> #<ActiveRecord::Relation []>
(Using Rails 4.0.4 with sqlite3 3.7.13 with gem version 1.3.9 and Ruby 2.0.0p353 on Mac OSX 10.9.2
app/models/report.rb
class Report < ActiveRecord::Base
has_many :records
end
app/models/record.rb
class Record < ActiveRecord::Base
belongs_to :report
has_many :related_publishers
end
app/models/related_publisher.rb
class RelatedPublisher < ActiveRecord::Base
belongs_to :record
end
From schema.rb
create_table "records", force: true do |t|
t.integer "leid"
t.integer "sid"
t.string "name"
t.string "url"
t.string "join_date"
t.string "join_ip"
t.string "country"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "report_id"
t.boolean "ipAddressMatch"
t.boolean "whoisAddressMatch"
t.integer "recommendation"
end
add_index "records", ["report_id"], name: "index_records_on_report_id"
create_table "related_publishers", force: true do |t|
t.integer "leid"
t.integer "sid"
t.string "name"
t.string "url"
t.string "join_date"
t.string "join_ip"
t.string "country"
t.integer "record_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "related_publishers", ["record_id"], name: "index_related_publishers_on_record_id"
create_table "reports", force: true do |t|
t.integer "report_type"
t.string "upload_data_file_path"
t.string "completed_data_file_path"
t.datetime "created_at"
t.datetime "updated_at"
t.string "ltn_username"
end
I have not seen #new when building an association. Quoting from the Active Record Associations docs you will find
4.3.1.14 collection.build(attributes = {}, ...)
The collection.build method returns one or more new objects of the associated type. These objects will be instantiated from the passed attributes, and the link through their foreign key will be created, but the associated objects will not yet be saved.
which seems to be what you want to achieve in your irb session. In your case this should translate into
#record.related_records.build(val: "related")
#record.save
(note that you save the parent, it will store its changed children too)
Alternatively you may use
4.3.1.15 collection.create(attributes = {})
The collection.create method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be created, and, once it passes all of the validations specified on the associated model, the associated object will be saved.
if the newly created record should be saved right away (and the parent object is saved already).
I have a User model with a draft_record boolean column which has a default of true. When creating records they are created with draft_record: false rather than true. This worked when the field was called draft however then the draft association and draft attribute assignment methods clashed resulting in the draft attribute being unsettable. Am I doing something wrong? I have had this problem before and just worked around it by reverting to what worked.
Ruby: 1.9.3-p327
Ruby on Rails: 3.2.12
DBMS: Postgres
Relevant migration:
class AddDraftColumnToUsers < ActiveRecord::Migration
def self.up
add_column :users, :draft_record, :boolean, default: true
add_column :users, :draft_id, :integer
add_column :users, :current_id, :integer
end
def self.down
...
end
end
Resultant schema
ActiveRecord::Schema.define(:version => 20130303002123) do
create_table "users", :force => true do |t|
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "name"
t.boolean "draft_record", :default => true
t.integer "draft_id"
t.integer "current_id"
end
end
Creating a user object:
Loading development environment (Rails 3.2.12)
1.9.3-p327 :001 > u = User.create(name: "Jon")
(0.0ms) begin transaction
SQL (28.8ms) INSERT INTO "users" ("created_at", "current_id", "draft_id", "draft_record", "name", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["created_at", Sun, 03 Mar 2013 00:42:04 UTC +00:00], ["current_id", nil], ["draft_id", nil], ["draft_record", false], ["name", "Jon"], ["updated_at", Sun, 03 Mar 2013 00:42:04 UTC +00:00]]
(0.7ms) commit transaction
=> #<User id: 1, created_at: "2013-03-03 00:42:04", updated_at: "2013-03-03 00:42:04", name: "Jon", draft_record: false, draft_id: nil, current_id: nil>
1.9.3-p327 :002 >
This problem was caused by a default_scope with the conditions hash set to draft_record: false. This forced any record being added through active record to set draft_record to false.
The default value is set at DB level. So the insert query will not generate the default. Check the records inserted if they have correct default value set.