Using Rails 5, I have an issue when I try to save my association between these 2 models : User and Provider
My models :
class User < ApplicationRecord
# Relations
has_and_belongs_to_many :providers
end
class Provider < ApplicationRecord
# Relations
has_and_belongs_to_many :users
accepts_nested_attributes_for :users
end
the Controller :
class ProvidersController < ApplicationController
def new
#provider = current_user.providers.new
end
def create
#provider = current_user.providers.new(provider_params)
if #provider.save
redirect_to root_path
else
render :new
end
end
private
def provider_params
params[:provider][:status] = 'name'
params.require(:provider).permit(:name, :status)
end
Form :
= simple_form_for #provider do |f|
= f.error_notification
= f.input :name, required: false
= f.button :submit
On the create action, a new provider is created but it is not linked to the current user. (no data is insterted in the join table)
I have no idea why I have this behavior.
In console, if i do something like :
#user = User.create(email: "user#test.com", password: "password")
#provider = #user.providers.create(name: "Provider1", status: "name")
#provider.save
then the association is correctly saved.
> #user.providers
=> #<ActiveRecord::Associations::CollectionProxy [#<Provider id: 17, name: "Provider1", status: "name", created_at: "2017-07-25 09:37:19", updated_at: "2017-07-25 09:37:19">]>
Thanks for any idea !
For info, my schema :
create_table "providers", force: :cascade do |t|
t.string "name"
t.string "status"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "providers_users", id: false, force: :cascade do |t|
t.bigint "provider_id", null: false
t.bigint "user_id", null: false
t.index ["provider_id", "user_id"], name: "index_providers_users_on_provider_id_and_user_id"
t.index ["user_id", "provider_id"], name: "index_providers_users_on_user_id_and_provider_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
Here is the log
ÈStarted POST "/providers" for 127.0.0.1 at 2017-07-25 11:49:27 +0200
Processing by ProvidersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"27O2Tz4bbqhfRmcuq+0DIZMebSaYVc6IO/uy889Z48fF1l3c8GfIZ+WcQvZKfUeEIB5+YbrM9dON2RH47p3TIQ==", "provider"=>{"name"=>"My new provider"}, "commit"=>"Sauvegarder"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 5], ["LIMIT", 1]]
(0.2ms) BEGIN
SQL (0.4ms) INSERT INTO "providers" ("name", "status", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "My new provider"], ["status", "name"], ["created_at", "2017-07-25 09:49:27.837165"], ["updated_at", "2017-07-25 09:49:27.837165"]]
(4.0ms) COMMIT
Redirected to http://localhost:3000/providers/18/steps.location
Completed 302 Found in 12ms (ActiveRecord: 5.0ms)
Started GET "/providers/18/steps.location" for 127.0.0.1 at 2017-07-25 11:49:27 +0200
Processing by Providers::StepsController#index as
Parameters: {"provider_id"=>"18"}
Redirected to http://localhost:3000/providers/18/steps/registration
Completed 302 Found in 2ms (ActiveRecord: 0.0ms)
Started GET "/providers/18/steps/registration" for 127.0.0.1 at 2017-07-25 11:49:27 +0200
Processing by Providers::StepsController#show as HTML
Parameters: {"provider_id"=>"18", "id"=>"registration"}
Provider Load (0.3ms) SELECT "providers".* FROM "providers" WHERE "providers"."id" = $1 LIMIT $2 [["id", 18], ["LIMIT", 1]]
Rendering providers/steps/registration.html.haml within layouts/provider
User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "providers_users" ON "users"."id" = "providers_users"."user_id" WHERE "providers_users"."provider_id" = $1 [["provider_id", 18]]
Rendered providers/steps/registration.html.haml within layouts/provider (13.2ms)
Rendered shared/_head.html.haml (30.6ms) [cache miss]
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 5], ["LIMIT", 1]]
Rendered shared/_header.html.haml (9.7ms) [cache miss]
Completed 200 OK in 69ms (Views: 64.7ms | ActiveRecord: 1.1ms)
I know maybe it is not the best solution but it is the fastest that comes to my mind
def create
#provider = current_user.providers.create(provider_params)
if #provider.id
redirect_to root_path
else
render :new
end
end
This will create join table record when you use new only why that will work in case of belongs to if provider belongs to user and has user_id in migration current_user.providers.new will add user_id in the new instance, in case has and belongs to many you can do it like this, maybe there is better way but this is what came up to me first.
or something like this
def create
#provider = Provider.new(provider_params)
if #provider.save
current_user.providers << #provider
redirect_to root_path
else
render :new
end
end
one more line but I guess looks cleaner.
def create
#provider = current_user.providers.new(provider_params)
#provider.users = [current_user]
if #provider.save
redirect_to root_path
else
render :new
end
end
I suggest using a has_many through relationship instead of has_many_and_belongs_to, because you can query the join table, and possibly add some more columns in the future in this join table, should the need arise.
P.S. This feels like a Rails bug / feature improvement to me. #provider.users = [current_user] should not be needed anymore as #provider = current_user.providers.new(provider_params) can already be inferred that the #provider object is associated already with current_user, and thus should be automatically assigned already. This already works with has_many though. Seems that only here in HABTM that it does not automatically assign.
Related
So as of this question I had a has_and_belongs_to_many working with a join table between people and occasions.
I had it working (with help from the lovely SO wizard that helped me), but said wizard recommended that a has_many through: :association might work better for my purposes. Unfortunately, it definitely does, but in shifting over I seem to have broken it.
My schema.rb now reads like this, using gifts to connect people and occasions:
create_table "gifts", force: :cascade do |t|
t.string "name"
t.decimal "price", precision: 8, scale: 2
t.string "store"
t.text "notes"
t.boolean "purchased", default: false
t.integer "user_id"
t.integer "person_id", null: false
t.integer "occasion_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["occasion_id"], name: "index_gifts_on_occasion_id"
t.index ["person_id"], name: "index_gifts_on_person_id"
t.index ["user_id"], name: "index_gifts_on_user_id"
end
create_table "occasions", force: :cascade do |t|
t.integer "person_id"
t.integer "user_id"
t.string "name"
t.date "date"
t.text "notes"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_occasions_on_user_id"
end
create_table "people", force: :cascade do |t|
t.string "relationship"
t.string "first_name"
t.string "middle_name"
t.string "last_name"
t.date "birthday"
t.date "anniversary"
t.date "other"
t.string "other_date_name"
t.text "notes"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "group"
t.integer "occasions_id"
t.index ["user_id"], name: "index_people_on_user_id"
end
I updated the models accordingly:
class Gift < ApplicationRecord
# Validations
validates_presence_of :person, :occasion, :user
# Relations
belongs_to :user
belongs_to :occasion
belongs_to :person
end
class Occasion < ApplicationRecord
# Relations
has_many :gifts
has_many :people, through: :gifts
belongs_to :user
end
class Person < ApplicationRecord
# Relations
belongs_to :user
has_many :gifts
has_many :occasions, through: :gifts
def full_name
"#{last_name}, #{first_name}"
end
end
My params would (I'm guessing) be where a potential issue might lie:
def occasion_params
params.require(:occasion).permit(:user_id, :name, :date, :notes, gift_ids: [])
end
and
def person_params
params.require(:person).permit(:relationship, :first_name, :middle_name, :last_name, :birthday, :anniversary, :other, :other_date_name, :notes, :group, :user_id, gift_ids: [])
end
This is taking place on my occasions#index page:
def index
#occasions = Occasion.where(user_id: current_user.id).order("date ASC")
#occasion = Occasion.new
#gifts = Gift.where(user_id: current_user.id)
end
And the occasion#create method looks like this:
def create
#occasion = Occasion.new(occasion_params)
#occasion.user_id = current_user.id
respond_to do |format|
if #occasion.save
format.html { redirect_to occasions_url, notice: 'Occasion was successfully created.' }
format.json { render :show, status: :created, location: #occasion }
else
format.html { render :new }
format.json { render json: #occasion.errors, status: :unprocessable_entity }
end
end
end
Finally, here's the server log when I attempt to add a new occasion:
Started POST "/occasions" for 127.0.0.1 at 2018-10-14 11:03:42 -0700
Processing by OccasionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0CaXl8VdEMITUv7/2ouzrF6ArOpe1Wh7QJS3pno8AdAeL834G9rFebfixgsXU7sQ7/HjzGh17yYWhtIGbN18jQ==", "occasion"=>{"name"=>"Here's a Test Occasion", "date"=>"2018-10-31", "notes"=>"", "person_ids"=>["", "22", "24", "25", "26", "27"]}, "commit"=>"Create Occasion"}
User Load (4.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ /Users/lizbayardelle/.rvm/gems/ruby-2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
Unpermitted parameter: :person_ids
(0.2ms) begin transaction
↳ app/controllers/occasions_controller.rb:35
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/occasions_controller.rb:35
Occasion Create (6.8ms) INSERT INTO "occasions" ("user_id", "name", "date", "notes", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["user_id", 1], ["name", "Here's a Test Occasion"], ["date", "2018-10-31"], ["notes", ""], ["created_at", "2018-10-14 18:03:43.003716"], ["updated_at", "2018-10-14 18:03:43.003716"]]
↳ app/controllers/occasions_controller.rb:35
(2.3ms) commit transaction
↳ app/controllers/occasions_controller.rb:35
Redirected to http://localhost:3000/occasions
Completed 302 Found in 131ms (ActiveRecord: 14.2ms)
Can anyone with an eye for :associations help me troubleshoot?
UPDATED SERVER LOG
Started POST "/occasions" for 127.0.0.1 at 2018-10-14 14:33:16 -0700
Processing by OccasionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"9z9jHd5I9Q+oiRVSxC8T2MpP/X99wcGt8/USh7oEdOc5NjlyAM8gtAw5LaYJ9xtkez6yWUthRvCl53cnrOUJug==", "occasion"=>{"name"=>"Test Occasion", "date"=>"2018-10-01", "notes"=>"", "person_ids"=>["", "22", "24", "25"]}, "commit"=>"Create Occasion"}
User Load (3.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ /Users/lizbayardelle/.rvm/gems/ruby-2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
Person Load (1.6ms) SELECT "people".* FROM "people" WHERE "people"."id" IN (?, ?, ?) [["id", 22], ["id", 24], ["id", 25]]
↳ app/controllers/occasions_controller.rb:31
(0.2ms) begin transaction
↳ app/controllers/occasions_controller.rb:35
User Load (1.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/occasions_controller.rb:35
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/occasions_controller.rb:35
CACHE User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/occasions_controller.rb:35
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/occasions_controller.rb:35
(0.1ms) rollback transaction
↳ app/controllers/occasions_controller.rb:35
Completed 422 Unprocessable Entity in 87ms (ActiveRecord: 6.9ms)
ActiveRecord::RecordInvalid - Validation failed: Gifts is invalid:
app/controllers/occasions_controller.rb:35:in `block in create'
app/controllers/occasions_controller.rb:34:in `create'
Started POST "/__better_errors/a57498aade504932/variables" for 127.0.0.1 at 2018-10-14 14:33:17 -0700
Unpermitted parameter: :person_ids
You are very close! You just need to change gift_ids[] to person_ids[] in occasion_params
def occasion_params
params.require(:occasion).permit(:user_id, :name, :date, :notes, person_ids: [])
end
Also, you shall encounter same error when you create a Person instance as you have gift_ids[] in person_params as well. Change it to occasion_ids[]
def person_params
params.require(:person).permit(:relationship, :first_name, :middle_name, :last_name, :birthday, :anniversary, :other, :other_date_name, :notes, :group, :user_id, occasion_ids: [])
end
Update:
ActiveRecord::RecordInvalid - Validation failed: Gifts is invalid
This error is due the Gift instance getting created with a nil user_id. You can add optional: true to the association to counter the error add a callback for Gift instance to create with a correct entry for user_id
#gift.rb
class Gift < ApplicationRecord
# Validations
validates_presence_of :person, :occasion
# Relations
belongs_to :user, optional: true
belongs_to :occasion
belongs_to :person
after_create :set_user_id
private
def set_user_id
self.update_column(:user_id, self.occasion.user_id)
end
end
Just started learning ruby on rails, and i created a simple shopping cart application but when i click "add to cart", i get a rollback transaction from my server. I believe the error has something to do with my orderitem controller but not sure how to fix this issue here my code.
rails server
Started POST "/order_items" for 127.0.0.1 at 2017-10-25 10:47:44 -0400
Processing by OrderItemsController#create as JS
Parameters: {"utf8"=>"✓", "order_item"=>{"product_id"=>"13", "quantity"=>"1"}, "commit"=>"Add to cart"}
(0.0ms) begin transaction
Product Load (0.5ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ? [["id", 13], ["LIMIT", 1]]
(0.0ms) rollback transaction
Rendering order_items/create.js.erb
Rendered order_items/create.js.erb (0.5ms)
Completed 200 OK in 1028ms (Views: 605.5ms | ActiveRecord: 0.5ms)
order_items_controller.rb
class OrderItemsController < ApplicationController
def create
#order = current_order
#order_item = #order.order_items.new(order_item_params)
#order.save
session[:order_id] = #order.id
end
def update
#order = current_order
#order_item = #order.order_items.new(order_item_params)
#order_item.update_attributes(order_item_params)
#order_items = #order.order_items
end
def destroy
#order = current_order
#order_item = #order.order_items.find(params[:id])
#order_item.destroy
#order_items = #order.order_items
end
private
def order_item_params
params.require(:order_item).permit(:product_id, :quantity)
end
end
create.js.erb
<% if #order.errors.any? || #order_item.errors.any? %>
alert("Invalid")
<% else %>
$(".cart").html("<%= escape_javascript(render 'layouts/cart') %>")
<% end %>
schema.rb
ActiveRecord::Schema.define(version: 20171019015705) do
create_table "order_items", force: :cascade do |t|
t.integer "product_id"
t.integer "order_id"
t.integer "quantity"
t.float "total_price"
t.float "unit_price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "orders", force: :cascade do |t|
t.float "subtotal"
t.float "total"
t.float "shipping"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "products", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.float "price"
t.text "description"
t.string "picture"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "username"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
end
orderItem.rb
class OrderItem < ApplicationRecord
belongs_to :order
belongs_to :product
before_save :set_unit_price
before_save :set_total_price
def unit_price
if persisted?
self[:unit_price]
else
product.price
end
end
def total_price
unit_price * quantity
end
private
def set_unit_price
self[:unit_price] = unit_price
end
def set_total_price
self[:total_price] = quantity * set_unit_price
end
end
order.rb
class Order < ApplicationRecord
has_many :order_items
belongs_to :user
before_save :set_subtotal
def subtotal
order_items.collect {|order_item| order_item.valid? ? (order_item.unit_price*order_item.quantity) : 0}.sum
end
private
def set_subtotal
self[:subtotal] = subtotal
end
end
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :orders
def to_param
username
end
end
carts_controller.rb
class CartsController < ApplicationController
before_action :authenticate_user!
def show
#order_items = current_order.order_items
end
end
Rails Server now
Started GET "/products" for 127.0.0.1 at 2017-10-25 11:21:11 -0400
Processing by ProductsController#index as HTML
Rendering products/index.html.erb within layouts/application
Product Load (0.0ms) SELECT "products".* FROM "products"
Rendered products/index.html.erb within layouts/application (16.0ms)
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Rendered layouts/_cart.html.erb (1.0ms)
Rendered layouts/_nav.html.erb (34.3ms)
Completed 200 OK in 604ms (Views: 571.1ms | ActiveRecord: 0.5ms)
#order_item = #order.order_items.new(order_item_params)
is creating an item only in memory. there is nothing wrong with that.but i think you need to change the new to create as so
#order_item = #order.order_items.create(order_item_params)
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.
My user id isn't saving and I do not understand why.
I added the associations to both models and added the user_id to the notification params but the user id does not save automatically.
The campus_id relation does work properly and I do not really see the difference. What could be the problem? I am using a postgress database.
rails server log
Rendered /usr/local/rvm/gems/ruby-2.1.1#rails4/gems/actionpack-4.0.2/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (59.4ms)
Started POST "/campus/1/notifications" for 84.193.153.106 at 2014-10-18 19:41:03 +0000
Started POST "/campus/1/notifications" for 84.193.153.106 at 2014-10-18 19:41:03 +0000
Processing by NotificationsController#create as HTML
Processing by NotificationsController#create as HTML
Parameters: {"utf8"=>"_", "authenticity_token"=>"L9RH3hZyAKqzq9/kuJJDNaEHNVca2DbQSKSZLc8iTuw=", "notification"=>{"post"=>"thisisatest"}, "commit"=>"Maak notificatie", "campu_id"=>"1"}
Parameters: {"utf8"=>"_", "authenticity_token"=>"L9RH3hZyAKqzq9/kuJJDNaEHNVca2DbQSKSZLc8iTuw=", "notification"=>{"post"=>"thisisatest"}, "commit"=>"Maak notificatie", "campu_id"=>"1"}
Campus Load (0.6ms) SELECT "campus".* FROM "campus" WHERE "campus"."id" = $1 LIMIT 1 [["id", "1"]]
Campus Load (0.6ms) SELECT "campus".* FROM "campus" WHERE "campus"."id" = $1 LIMIT 1 [["id", "1"]]
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
(0.2ms) BEGIN
(0.2ms) BEGIN
SQL (134.0ms) INSERT INTO "notifications" ("campus_id", "created_at", "post", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["campus_id", 1], ["created_at", Sat, 18 Oct 2014 19:41:03 UTC +00:00], ["post", "thisisatest"], ["updated_at", Sat, 18 Oct 2014 19:41:03 UTC +00:00]]
SQL (134.0ms) INSERT INTO "notifications" ("campus_id", "created_at", "post", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["campus_id", 1], ["created_at", Sat, 18 Oct 2014 19:41:03 UTC +00:00], ["post", "thisisatest"], ["updated_at", Sat, 18 Oct 2014 19:41:03 UTC +00:00]]
(42.1ms) COMMIT
(42.1ms) COMMIT
Redirected to https://sl-backoffice-c9-christoph88.c9.io/notifications/9
Redirected to https://sl-backoffice-c9-christoph88.c9.io/notifications/9
Completed 302 Found in 188ms (ActiveRecord: 177.4ms)
Completed 302 Found in 188ms (ActiveRecord: 177.4ms)
Notification model
class Notification < ActiveRecord::Base
belongs_to :campus
belongs_to :user
validates :post, presence: true
validates :campus_id, presence: true
end
User model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable
has_many :notifications
has_many :spotlights
end
notifications controller
class NotificationsController < ApplicationController
before_action :set_notification, only: [:show, :edit, :update, :destroy]
before_action :set_campus, only: [:index, :new, :create]
before_action :authenticate_user!
def index
#notifications = #campus.notifications
end
def show
#campus = #notification.campus
end
def new
#notification = #campus.notifications.build
end
def edit
end
def create
#notification = #campus.notifications.build(notification_params)
respond_to do |format|
if #notification.save
format.html { redirect_to #notification }
format.json { render action: 'show', status: :created, location: #notification }
flash[:success] = 'Notification was successfully created.'
else
format.html { render action: 'new' }
format.json { render json: #notification.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #notification.update(notification_params)
format.html { redirect_to #notification }
format.json { head :no_content }
flash[:success] = 'Notification was successfully updated.'
else
format.html { render action: 'edit' }
format.json { render json: #notification.errors, status: :unprocessable_entity }
end
end
end
def destroy
#notification.destroy
respond_to do |format|
format.html { redirect_to campu_notifications_url(#notification.campus_id) }
format.json { head :no_content }
flash[:error] = 'Notification was successfully deleted.'
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_notification
#notification = Notification.find(params[:id])
end
def set_campus
#campus = Campus.find(params[:campu_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def notification_params
params.require(:notification).permit(:post, :campus_id, :user_id)
end
end
schema.rb
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20141018121628) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "campus", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "collis", force: true do |t|
t.string "name"
t.text "teaser"
t.text "description"
t.string "coursetype"
t.text "target"
t.string "campus"
t.datetime "startdate"
t.datetime "created_at"
t.datetime "updated_at"
t.string "guid"
end
create_table "coursespotlights", force: true do |t|
t.boolean "spotlight"
t.integer "colli_id"
t.datetime "start"
t.datetime "stop"
t.string "colli_guid"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "coursespotlights", ["colli_id"], name: "index_coursespotlights_on_colli_id", using: :btree
create_table "notifications", force: true do |t|
t.text "post"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "campus_id"
t.integer "user_id"
end
add_index "notifications", ["campus_id"], name: "index_notifications_on_campus_id", using: :btree
add_index "notifications", ["user_id"], name: "index_notifications_on_user_id", using: :btree
create_table "spotlights", force: true do |t|
t.boolean "spotlight"
t.datetime "start"
t.datetime "stop"
t.string "name"
t.text "teaser"
t.datetime "coursedate"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "campus_id"
t.integer "user_id"
end
add_index "spotlights", ["user_id"], name: "index_spotlights_on_user_id", using: :btree
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
end
Because user_id is not a part of your parameters
From the log:
Parameters: {"utf8"=>"_", "authenticity_token"=>"L9RH3hZyAKqzq9/kuJJDNaEHNVca2DbQSKSZLc8iTuw=", "notification"=>{"post"=>"thisisatest"}, "commit"=>"Maak notificatie", "campu_id"=>"1"}
then the line from the strong params filters out unwanted content
params.require(:notification).permit(:post, :campus_id, :user_id)
this means only "notification"=>{"post"=>"thisisatest"} part will be allowed
then from the sql insert:
SQL (134.0ms) INSERT INTO "notifications" ("campus_id", "created_at", "post", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["campus_id", 1], ["created_at", Sat, 18 Oct 2014 19:41:03 UTC +00:00], ["post", "thisisatest"], ["updated_at", Sat, 18 Oct 2014 19:41:03 UTC +00:00]]
you have campus_id here because you are doing the update on #campus:
#campus.notifications.build(notification_params)
so, just make sure user_id comes in as part of notification and that is it!