I am working on creating shop cart / order. I created shop cart with association by this guide so I have some problem. After a new sign in session My app can't find my previous cart and create one new
module ApplicationHelper
def current_cart
if !session[:cart_id].nil?
Cart.find(session[:cart_id])
else
Cart.new
end
end
end
Cart_items_controller
class CartItemsController < ApplicationController
def create
#cart = current_cart
#cart_item = #cart.cart_items.new(cart_params)
#cart.save
redirect_back fallback_location: root_path
flash[:success] = "Items added to your cart"
session[:cart_id] = #cart.id
end
def destroy
#cart = current_cart
#cart_item = #cart.cart_items.find(params[:id])
#cart_item.destroy
#cart_items = current_cart.cart_items
redirect_to carts_path
end
private
def cart_params
params.require(:cart_item).permit(:product_id, :user_id)
end
end
Cart Controller
class CartsController < ApplicationController
def index
end
def show
#cart_items = current_cart.cart_items
#order_item = current_order.order_items.new
end
def new
end
end
My module association
class CartItem < ApplicationRecord
belongs_to :cart
belongs_to :product
end
class Cart < ApplicationRecord
has_many :cart_items
end
class User < ApplicationRecord
has_one :cart
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
class Product < ApplicationRecord
has_one_attached :image
has_many :order_items
has_many :cart_items
end
Product controller show
def show
#product = Product.find(params[:id])
#cart_item = current_cart.cart_items.new
end
Product view show
...
<%= form_for #cart_item, remote: true do |f|%>
<%= f.hidden_field :product_id, :value => #product.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.submit "Add to Card ", class: "btn btn-primary"%>
<% end %>
So my Schema
create_table "cart_items", charset: "utf8mb3", force: :cascade do |t|
t.integer "cart_id"
t.integer "product_id"
t.integer "user_id"
end
create_table "carts", charset: "utf8mb3", force: :cascade do |t|
t.integer "user_id"
end
create_table "products", charset: "utf8mb3", force: :cascade do |t|
t.string "title"
t.string "description"
t.integer "price"
end
create_table "users", charset: "utf8mb3", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "name"
end
So what I get after adding to cart.
enter image description here
I think problem with finding session[:cart_id] It return nil and after every sign in it create a new cart
Can anyone help me?
Your current_cart helper is creating a new cart any time there isn't one in the session. In a new session, there of course won't be a cart, even if the user has had one previously, so if you want to use their latest cart instead of always creating a new one, you could do something like this:
def current_cart
if session[:cart_id].present?
Cart.find(session[:cart_id])
else
current_user.cart || current_user.build_cart
end
end
You should probably also make sure that a cart can't be created without an associated user, or you might end up with orphaned carts / cart items in your database, like in the screenshot you posted. The cart should have a belongs_to :user association, and user_id could be a foreign key with null: false on the database layer if you wanted to be extra safe. In your User model, the relation to cart could be has_one :cart, dependent: :destroy to make sure that the user's cart is destroyed when the user gets destroyed.
Also, it seems redundant to have a user_id in your cart_items table, since the user will of course be the same as in the associated cart. A has_one :user, through: :cart association should take care of that.
Related
I'm new to RoR and I want to create simple page like a task manager (to add and remove tasks) so I created 2 tables with association between them (Track and Item).
Here is 2 models:
class Item < ApplicationRecord
belongs_to :track, optional: :true
end
class Track < ApplicationRecord
has_many :items, dependent: :destroy
end
And I need to set association when I create or delete any track item. But when I create it I just see my track item (with an empty field in associated table)
For example:
rails c
Track.create(item: 'Asafa Pauel', description: 'This is a description') - works fine (added all field to db)
Item.all - track_id field is empty - but it should show id of track item. Why is this?
And my Tracks controller:
class TracksController < ApplicationController
def index
#track = Track.all
end
def show
#track = Track.all
end
def new
#track = Track.new
end
def create
#track = Track.new(track_params)
#item = Item.new(track_id: #track.id)
if #track.save! && #item.save!
flash[:success] = "It works!"
redirect_to tracks_path
else
flash[:success] = "Its wrong!"
end
end
private
def track_params
params.require(:track).permit(:item, :description)
end
end
And Items controller:
class ItemsController < ApplicationController
def create
#item = Item.new(item_params)
end
private
def item_params
params.require(:item).permit(:track_id)
end
end
And db schema:
ActiveRecord::Schema.define(version: 2019_05_23_112947) do
enable_extension "plpgsql"
create_table "items", force: :cascade do |t|
t.bigint "track_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["track_id"], name: "index_items_on_track_id"
end
create_table "tracks", force: :cascade do |t|
t.string "item"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Thanks in advance
Your new 'Track' object doesn't have an ID yet, so you can't assign its value to Item.track_id.
First you'll have to save the Track, then create a new Item.
Also, if you create a new Track from console, you won't trigger your "create" method in the controller: it will be called only if you create a new Track from browser.
If you want to create a new Item every time you create a Track, you'll have to do something like this in your model file "track.rb":
after_save :create_new_item
def create_new_item
self.items.create
end
P.S.: the "track.rb" file is in "app/models" in your Rails application.
I'm quite new to programming and haven't been able to find any resources to help me with this. I have created two scaffolds Accounts and Cashbooks. I want users to be able to add an account (with a parameter of account_name) which will then be set as the parameters for the Cashbook account input. Ideally when adding a Cashbook transaction, I would want users to see a dropdown which will contain all the account_names of the Accounts which have been created.
Cashbook Model
class Cashbook < ActiveRecord::Base
belongs_to :account, foreign_key: :account_name
end
Cashbook Controller
def new
#cashbook = Cashbook.new
#cashbook.account(account_params[:account_name])
end
def create
#cashbook = Cashbook.new(cashbook_params)
#cashbook.account(account_params[:account_name])
def cashbook_params
params.require(:cashbook).permit(:date, :description, :account, :kind, :amount, :balance, :name)
params.require(:account).permit(:account_name)
end
Cashbook DB
class CreateAccounts < ActiveRecord::Migration
def change
create_table :accounts do |t|
t.string :account_name
t.boolean :operating_expense
t.boolean :cost_of_sales
t.boolean :sales
t.boolean :other_income
t.boolean :non_current_liability
t.boolean :non_current_asset
t.boolean :current_asset
t.boolean :current_liability
t.boolean :equity
t.integer :account_number
t.timestamps null: false
end
end
end
Account Model
class Account < ActiveRecord::Base
has_many :cashbook, foreign_key: :account_name
end
Account Controller
def new
#account = Account.new
end
def edit
end
def create
#account = Account.new(account_params)
end
def account_params
params.require(:account).permit(:account_name, :operating_expense, :cost_of_sales, :sales, :other_income, :non_current_liability, :non_current_asset, :current_asset, :current_liability, :equity, :account_number)
end
Cashbook db
class CreateCashbooks < ActiveRecord::Migration
def change
create_table :cashbooks do |t|
t.date :date
t.string :description
t.boolean :account
t.string :kind
t.integer :amount
t.timestamps null: false
end
end
end
You can use the collection_select form helper.
In your case, it could look something like
<%= collection_select(:cashbook, :account_name, Account.all, :account_name, :account_name) %>
Though you should also consider using the id column to associate models as per the Rails Associations Guide. If you change your models to use ids as the foreign key, you won't need to explicitly specify the foreign key in your has_many and belongs_to statements, and you can still create a select box that displays account names:
<%= collection_select(:cashbook, :account_id, Account.all, :id, :account_name) %>
I have setup has_many and has_many :through association between a Order,User,Product and Order_detail model as a join table.
Models:
class Order < ActiveRecord::Base
has_many :order_details
belongs_to :user
has_many :products, through: :order_details
end
class OrderDetail < ActiveRecord::Base
belongs_to :order
belongs_to :product
end
class Product < ActiveRecord::Base
has_many :order_details
has_many :orders, through: :order_details
end
class User < ActiveRecord::Base
has_many :orders
end
How to save automatically for join table order_details.
Now data save only to order table.
Need to save all products to order_tables for current order and user
class OrdersController < ApplicationController
before_action :authenticate_user!
def index
#order = Order.all
end
def new
# #order = Order.new
#order = current_user.orders.new
end
def create
#order = current_user.orders.new(order_params)
#order.date = Date.today.to_s
if #order.save
# i think this is bad wrong to implementation of functional)
# params[:product_id].each do |detail_product_id|
# #order.order_details.product_id = detail_product_id
# #order.order_details.user_id = current_user
# #order.order_details.save
flash[:success] = "Order was successfully submitted"
redirect_to new_order_path
else
render :new
end
end
private
def order_params
params.require(:order).permit(:date, :product_id => [])
end
end
My schema:
create_table "order_details", force: true do |t|
t.integer "order_id"
t.integer "product_id"
t.integer "quantity"
t.integer "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "orders", force: true do |t|
t.integer "user_id"
t.date "date"
end
add_index "orders", ["user_id"], name: "index_orders_on_user_id", using: :btree
create_table "orders_products", id: false, force: true do |t|
t.integer "order_id"
t.integer "product_id"
end
create_table "products", force: true do |t|
t.string "product_name"
t.integer "product_price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "available_status"
t.string "product_type"
end
In your view, add fields for order_details like:
<%= f.fields_for :order_details do |od| %>
<%= od.label 'your attribute for OrderDetail' %>
<%= # od.text_field 'your attribute' %>
<% end %>
Then in your model, accept nested attributes for order_details
like:
accepts_nested_attributes_for :order_details
These are sample values, you can use this logic with your actual attributes.
In your controller, permit attributes for order_details like:
params.require(:order).permit(:id, :name, order_details: [
#attributes of order details
])
Assuming that product_ids is an array of the product ids that you wish to add to the order, you could try assigning them in the following way and Rails should automagically create those association records for order_details when you then call #order.save
#order.products << Product.find_by_id(product_ids)
Am i right add that rows for controller?
Order_controller:
def create
#order = current_user.orders.new(order_params)
#order.products = Product.find_by_id(order_params[:product_ids])
#order.date = Date.today.to_s
if #order.save
flash[:success] = "Order was successfully submitted"
redirect_to new_order_path
else
render :new
end
end
private
def order_params
params.require(:order).permit(:date, order_details: [:product_id])
end
end
order model:
accepts_nested_attributes_for :order_details
I'm trying to save from 3 different types of products.
But how to take one product id from each part ? Because now I can choose only one product from all
View:
= simple_form_for(#order, html: {:class => 'well form-horizontal', :method => :post, :action=> :create }) do |f|
.col-xs-12.col-sm-6.col-md-8
= render 'shared/error_messages', object: f.object
%br
= simple_fields_for :order_details do |od|
= od.collection_radio_buttons :product_ids, Product.get_first_course, :id, :product_name ,:item_wrapper_class => 'inline'
%hr
= od.collection_radio_buttons :product_ids, Product.get_main_course, :id, :product_name, :item_wrapper_class => 'inline'
%hr
= od.collection_radio_buttons :product_ids, Product.get_drink, :id, :product_name,:item_wrapper_class => 'inline'
%hr
= f.button :submit, class: "btn btn-primary"
I change my association type to HABTM and that's enough for my situation. So..
models:
class Order < ActiveRecord::Base
has_and_belongs_to_many :products
before_destroy { products.clear }
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :orders
end
Order_controller:
def create
order = current_user.orders.new(date: Date.today.to_s)
#order_products = Product.where(id: order_params[:product_ids])
order.products << #order_products
if order.save
#blalblal - sucsess
else
#blabla - alert-notice
end
I have a rails application with
1)User Model
class User < ActiveRecord::Base
has_secure_password
has_many :projects
end
2) Project Model
class Project < ActiveRecord::Base
belongs_to :user
end
3) CreateUser in db/migrate
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.string :password_digest
t.references :projects
t.timestamps null: false
end
end
end
4) CreateProject in db/migrarte
class CreateProjects < ActiveRecord::Migration
def change
create_table :projects do |t|
t.string :name
t.string :description
t.references :users
t.timestamps null: false
end
end
end
Now in my Controller, I have a function
def create
#project = Project.new(project_params)
#user = User.find(session[:user_id])
if #project.save
#user.projects << Project.find(#project.id)
redirect_to '/'
else
redirect_to '/project/create'
end
end
But when i call http://localhost:3000/project/new, I receive following error :-
-NoMethodError in ProjectController#create
-undefined method `projects' for # User
with
#user.projects << Project.find(#project.id)
highlighted in the extracted source.
Am I entering the record into has_many relationship correct, or is my syntax wrong?
I ran the following code in the console the server,
user = User.find(1)
user.projects
I received this error message:
NoMethodError: undefined method `projects' for #<User:0x00000001f5b508>
from /home/harshil/.rvm/gems/ruby-2.2.1/gems/activemodel-4.2.4/lib/active_model/attribute_methods.rb:433:in `method_missing'
from /home/harshil/.rvm/gems/ruby-2.2.1/gems/activemodel-4.2.4/lib/active_model/attribute_methods.rb:433:in `method_missing'
Thanks
It seems the CreateUser migration is incorrect. It should not reference the projects. Projects should reference the User, which you have done correctly.
I believe this is confusing ActiveRecord
try removing t.references :projects from the UserCreate migration and try again
Having an invoice model which belongs to user, and a user table that associates a user as a particular role or multiple roles:
class CreateInvoices < ActiveRecord::Migration
def self.up
create_table :invoices do |t|
t.string :po_number
t.datetime :invoice_date
t.datetime :date_received
t.datetime :date_approved
t.text :clerk_note
t.integer :clerk_id
t.integer :approver_id
t.text :approver_note
end
end
class Invoice < ActiveRecord::Base
belongs_to :user
end
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :username
t.string :email
t.boolean :account_clerk
t.boolean :approver
t.boolean :admin
end
end
class User < ActiveRecord::Base
has_many :invoices
end
In the invoice record, how do I assign an clerk_id and approver_id depending on the role set in the user model? Each invoice will have a clerk and approver, but both are users.
Likewise, how do I assign the clerk_note to the clerk_id and the approver_note to the approver_id? I'm assuming in this case, I can just refer to current_user, since the user logged in will be the one making the note, right?
I'm not sure what this is called, so in my Googling, I didn't know what to look for... thanks in advance for your help.
To answer your original question using your current models you can just create your view to display different fields based on the role of the user (this assumes the logged in user is in an instance variable named #user).
<% if (#user.approver) then %>
<%= f.text_area :approver_note %>
<% else %>
<%= f.text_area :clerk_note %>
<% end %>
Then in your controller you can set the value of clerk id or approver id again depending on the logged in user.
if (#user.approver) then
#invoice.approver_id = #user.id
else
#invoice.clerk_id = #user.id
end