Paperclip Nested attributes on polymorphic model not getting saved (RAILS) - ruby-on-rails

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%>

Related

Model is invalid - probably something's wrong with association

I want to create app for my own usage.
I want to encourage my kids to eat more vegetables and fruits by creating a game. Game should have players, snacks and date. Every time, when game is created it should randomly assign selected in game snacks to selected in game players. When player eat randomly assigned snack, they gain points assigned to each snack and when player collect x points, they can exchange it for award. Each player has many games and could have many snacks in each game and each snack could belong to many games and to many players in each game.
I'm self learning beginner at rails, so if you have idea's how to modify it all, I'll be thankful for suggestions.
After few suggestions by random people, I got implement it all as below.
So I'm stack at creating game, because I don't know how to after creating the game assign selected snacks to players because their id's are not saved and I don't know how to modify it. Probably I could change association's to be a mirror's like players associaton's but I'm not quite sure is it a good way.
Processing by GamesController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "game"=>{"game_date(1i)"=>"2022", "game_date(2i)"=>"5", "game_date(3i)"=>"18", "player_ids"=>["1"], "snack_ids"=>["1"]}, "commit"=>"Create Game"}
Unpermitted parameter: :snack_ids. Context: { controller: GamesController, action: create, request: #<ActionDispatch::Request:0x00007fad6c1eed50>, params: {"authenticity_token"=>"[FILTERED]", "game"=>{"game_date(1i)"=>"2022", "game_date(2i)"=>"5", "game_date(3i)"=>"18", "player_ids"=>["1"], "snack_ids"=>["1"]}, "commit"=>"Create Game", "controller"=>"games", "action"=>"create"} }
Player Load (0.6ms) SELECT "players".* FROM "players" WHERE "players"."id" = ? [["id", 1]]
↳ app/controllers/games_controller.rb:27:in `create'
TRANSACTION (0.1ms) begin transaction
↳ app/controllers/games_controller.rb:30:in `block in create'
Player Exists? (0.5ms) SELECT 1 AS one FROM "players" WHERE "players"."name" = ? AND "players"."id" != ? LIMIT ? [["name", "Tata"], ["id", 1], ["LIMIT", 1]]
↳ app/controllers/games_controller.rb:30:in `block in create'
Game Create (1.4ms) INSERT INTO "games" ("game_date", "created_at", "updated_at") VALUES (?, ?, ?) [["game_date", "2022-05-18"], ["created_at", "2022-05-18 11:04:41.815456"], ["updated_at", "2022-05-18 11:04:41.815456"]]
↳ app/controllers/games_controller.rb:30:in `block in create'
PlayerGame Create (0.9ms) INSERT INTO "player_games" ("player_id", "game_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["player_id", 1], ["game_id", 10], ["created_at", "2022-05-18 11:04:41.820295"], ["updated_at", "2022-05-18 11:04:41.820295"]]
↳ app/controllers/games_controller.rb:30:in `block in create'
TRANSACTION (8.8ms) commit transaction
↳ app/controllers/games_controller.rb:30:in `block in create'
Redirected to http://localhost:3000/games/10
Completed 302 Found in 38ms (ActiveRecord: 13.3ms | Allocations: 9505)
Started GET "/games/10" for 127.0.0.1 at 2022-05-18 13:04:41 +0200
Processing by GamesController#show as TURBO_STREAM
Parameters: {"id"=>"10"}
Game Load (0.6ms) SELECT "games".* FROM "games" WHERE "games"."id" = ? LIMIT ? [["id", 10], ["LIMIT", 1]]
↳ app/controllers/games_controller.rb:66:in `set_game'
Rendering layout layouts/application.html.erb
Rendering games/show.html.erb within layouts/application
Rendered games/_game.html.erb (Duration: 1.3ms | Allocations: 222)
Rendered games/show.html.erb within layouts/application (Duration: 13.1ms | Allocations: 1433)
Rendered layout layouts/application.html.erb (Duration: 36.0ms | Allocations: 3738)
Completed 200 OK in 53ms (Views: 40.6ms | ActiveRecord: 0.6ms | Allocations: 5405)
Models associations looks like that :
class Game < ApplicationRecord
validates :game_date, comparison: { greater_than_or_equal_to: Date.today }
# after_create :assign_snacks_to_players
has_many :player_games
has_many :players, through: :player_games
has_many :snack_games
has_many :snacks, through: :snack_games
accepts_nested_attributes_for :snacks
end
class Player < ApplicationRecord
validates :name, length: {minimum: 3, maximum: 20}, uniqueness: true
validates :points, presence: true
validates :active, inclusion: { in: [true, false] }
has_many :player_games
has_many :games, through: :player_games
has_many :snack_games
has_many :snacks, through: :snack_games
end
class Snack < ApplicationRecord
validates :name, length: {minimum: 3, maximum: 20}, uniqueness: true
validates :points, presence: true
validates :active, inclusion: { in: [true, false] }
has_many :snack_games
has_many :games, through: :snack_games
end
class PlayerGame < ApplicationRecord
belongs_to :game
belongs_to :player
end
class SnackGame < ApplicationRecord
belongs_to :snack
belongs_to :game
belongs_to :player
end
Schema :
# 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.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2022_05_11_213227) do
create_table "games", force: :cascade do |t|
t.date "game_date", default: "2022-05-17", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "player_games", force: :cascade do |t|
t.integer "player_id", null: false
t.integer "game_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["game_id"], name: "index_player_games_on_game_id"
t.index ["player_id"], name: "index_player_games_on_player_id"
end
create_table "players", force: :cascade do |t|
t.string "name", null: false
t.integer "points", default: 0, null: false
t.boolean "active", default: true, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "snack_games", force: :cascade do |t|
t.integer "snack_id", null: false
t.integer "game_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "player_id", null: false
t.index ["game_id"], name: "index_snack_games_on_game_id"
t.index ["player_id"], name: "index_snack_games_on_player_id"
t.index ["snack_id"], name: "index_snack_games_on_snack_id"
end
create_table "snacks", force: :cascade do |t|
t.string "name", null: false
t.integer "points", default: 0, null: false
t.boolean "active", default: true, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_foreign_key "player_games", "games"
add_foreign_key "player_games", "players"
add_foreign_key "snack_games", "games"
add_foreign_key "snack_games", "players"
add_foreign_key "snack_games", "snacks"
end
I think player and snack controller's are not necessary so I include only games_controller
class GamesController < ApplicationController
before_action :set_game, only: %i[ show edit update destroy ]
# Find solution to assign players and snacks after create game not only from view but also from terminal
# GET /games or /games.json
def index
#games = Game.all
end
# GET /games/1 or /games/1.json
def show
end
# GET /games/new
def new
#game = Game.new
end
# GET /games/1/edit
def edit
end
# POST /games or /games.json
def create
#game = Game.new(game_params)
respond_to do |format|
if #game.save
format.html { redirect_to game_url(#game), notice: "Game was successfully created." }
format.json { render :show, status: :created, location: #game }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #game.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /games/1 or /games/1.json
def update
respond_to do |format|
if #game.update(game_params)
format.html { redirect_to game_url(#game), notice: "Game was successfully updated." }
format.json { render :show, status: :ok, location: #game }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #game.errors, status: :unprocessable_entity }
end
end
end
# DELETE /games/1 or /games/1.json
def destroy
#game.destroy
respond_to do |format|
format.html { redirect_to games_url, notice: "Game was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_game
#game = Game.find(params[:id])
end
# Only allow a list of trusted parameters through.
def game_params
params.require(:game).permit(:game_date, player_ids: [], player_attributes: [:name])
end
end
and here is a form, but not correct with :snack_ids
<%= form_with(model: game) do |form| %>
<% if game.errors.any? %>
<div style="color: red">
<h2><%= pluralize(game.errors.count, "error") %> prohibited this game from being saved:</h2>
<ul>
<% game.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= form.label :game_date, style: "display: block" %>
<%= form.date_select :game_date %>
</div>
<div>
<%= form.collection_check_boxes :player_ids, Player.where(active: true), :id, :name, include_hidden: false %>
</div>
<div>
<%= form.collection_check_boxes :snack_ids, Snack.where(active: true), :id, :name, include_hidden: false %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
I know I could associate snack with player by creating in IRB for game SnackGame with player id and snack id but I dont know how to handle it in form and model

Has_many through :association Not Registering in Rails 5.2

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

Rails server saying rollback transcation when clicking add to cart

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)

Association doesn't save between 2 models

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.

Rails 3 before_save not changing attributes

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>

Resources