Rails noob here.
I'm building a basic shopping cart and it was working perfectly before. Without changing any code (I git reset --hard to my prev commit where it was working) it broke. (?!?) Here's the breakdown:
Github Repo: https://github.com/christinecha/michaka
Creates a product. ✓
Adds Product ID to a new Order Item. ✓
Adds Order Item to an Order. ✓
--
Possible Issues
! - New Orders keep being created as you create Order Items = cart is always empty.
! - Cart is not connecting to the right Order ID
! - New sessions are being triggered = new Orders = problem
--
ORDER ITEMS CONTROLLER
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.find(params[:id])
#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(:quantity, :product_id)
end
end
SESSION_STORE.RB
Rails.application.config.session_store :cookie_store, key: '_bead-project_session'
ORDER MODEL
class Order < ActiveRecord::Base
belongs_to :order_status
has_many :order_items
before_create :set_order_status
before_save :update_subtotal
def subtotal
order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.unit_price) : 0 }.sum
end
def subtotal_cents
subtotal * 100
end
private
def set_order_status
self.order_status_id = 1
end
def update_subtotal
self[:subtotal] = subtotal
end
end
APPLICATION CONTROLLER
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_order
def current_order
if !session[:order_id].nil?
Order.find(session[:order_id])
else
Order.new
end
end
end
It looks like ProductsController#create is called twice, once with format html and once as json.
I think you're submitting some of your data via ajax but still doing a post request from the form. However your controller, in it's format.html response is redirecting before all of the javascript actions have completed.
Since you only save #order and set the session from OrderItemsController#create which is called by js after your initial ajax().success, it is incomplete when the redirect is received.
What I think happens on click:
ajax post request AND regular form post
ajax success -> submit #order_item_product_id form
redirected by original form post response
I would suggest either redesigning the submit process to submit through regular form post or entirely through js. For example you could disable post from the form and change OrderItemsController#create to finally redirect (via js) render :js => "window.location.href = '/cart';"
Related
I'm trying to call some ActiveRecord methods in OrdersController#new action.
I've tested the code in Rails Console and it works the way I intended.
But in the controller action it does not produce the same
order has_many cart_items
cart has_many cart_items
cart_items belong_to order cart_items
belong_to cart
cart_items belong_to product
product has_many cart_items
def new
#order.new
#order.cart_items = current_cart.cart_items
#order.save
current_cart.cart_items.destroy_all
end
Now current_cart is an application_controller method that checks if the current user has a shopping cart. If it does it pulls that cart from the database and if the user does not then it creates a new cart for the user. What I am trying to do here is when the user finalizes their order I'm trying to transfer the cart_items from current_cart to orders then clear the shopping cart.
When I do this in rails console, it gives me what I want. Order with cart_items that were in current_cart, and after I run destroy_all on the cart I have an empty active record association array.
When I test this in my controller both Order and Cart return an empty active association array.
What is going on here?
#application controller method of finding current_users cart
def current_cart
# if user is logged in
if current_user
#user = current_user
# checking user to see if account is confirmed and verified
if #user.confirmed_at != nil
# checking if user already has cart in cart database
if Cart.find_by(users_id: #user.id) != nil
# find a row in the database where users_id: equal to #user.id
# where clause does not work here
cart = Cart.find_by(users_id: #user.id)
session[:cart_id] = cart.id
cart.save
#establish Cart session cart for user
Cart.find(session[:cart_id])
else
# create a new Cart Object for user.assign current_user's id to cart object
cart = Cart.new
cart.users_id = #user.id
# save it to get cart id assign session[:cart_id] == cart.id
cart.save
session[:cart_id] = cart.id
end
end
end
end
class CartItemsController < ApplicationController
before_action :set_cart_item, only: [:show, :edit, :update, :destroy]
# scope for most_recent and subtotal
# find out if rails sorts on update column cuz this is annoying.
def create
# grabbing cart from application controller current_cart method
#cart = current_cart
# session[:cart_id] = #cart.id
# individual product items get added to cart item and added to cart and saved
#cart_item = #cart.cart_items.build(cart_item_params)
#cart.save
end
def update
#cart = current_cart
# finding cart_items by cart_id
#cart_item = #cart.cart_items.find(params[:id])
# #cart_items.order(:id)
#cart_item.update_attributes(cart_item_params)
#cart_items = #cart.cart_items.order(:id)
# redirect 'cart_show_path'
#cart.save
end
def destroy
#cart = current_cart
#cart_item = #cart.cart_items.find(params[:id])
#cart_item.destroy
#cart_items = #cart.cart_items
#cart.save
end
private
def set_cart_item
#cart_item = CartItem.find(params[:id])
end
def cart_item_params
params.require(:cart_item).permit(:cart_id, :product_id, :unit_price, :quantity, :total_price)
end
end
When you say you are transferring the cart_items from current_cart you are passing on the objects, you are not creating new cart_items (which means the database ids are same) and when you do current_cart.cart_items.destroy_all it is deleting them from the database. See ActiveRecord::Relation#destroy_all
For you use case, its enough if you just do
def new
#order.new
#order.cart_items = current_cart.cart_items
#order.save
current_cart.cart_items = []
end
Alright, figured it out.
I was using #order.save which was not processing the error message!!!!!!!!
After #order.save! it gave me the validation error in another model.
I commented that out and it worked.
I iterated current.cart_items and assigned the cart_id to nil and order_id to #order.id essentially clearing the cart and "transferring" the items over.
I couldn't figure out a way using destroy_all though. I think this is impossible like #Sri said.
Thanks alot!
So the end code was this:
#order = Order.create!(users_id: current_cart.users_id)
current_cart.cart_items.each do |item|
item.order_id = #order.id
item.save!
end
if #order.save
current_cart.cart_items.each do |item|
item.cart_id = nil
item.save!
end
I currently have a Subscriber model that takes in a "phone_number" and a "visit" attribute that is an integer. I want to set up a "check in" view form that will have a subscriber type in their phone_number and it will say if phone_number exists? add 1 to the visit attribute. So it will run a sql query and see if that number is in the database.
To be more clear I have to break the REST actions because the create action is already taken for the new subscribers. I'm pretty new to rails and I'm having a super hard time figuring this feature out. I'm curious if this is possible and how I should go about implementing this?
Here is my controller at the moment:
class SubscribersController < ApplicationController
def index
#subscriber = Subscriber.all
end
def new
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.create(subscriber_params)
if #subscriber.save
flash[:success] = "Subscriber Has Been successfully Created"
redirect_to new_subscriber_path(:subscriber)
else
render "new"
end
end
def visit
end
private
def subscriber_params
params.require(:subscriber).permit(:first_name, :last_name, :email, :phone_number)
end
end
Something along those lines?
def visit
subscriber = Subscriber.find_by_phone_number(params[:phone_number])
if subscriber
subscriber.visit += 1
subscriber.save
end
end
Make sure that the default value (via DB/Migration) for visit is set to 0.
You don't need to break REST style controller though. You can create another controller which does that check. For example something like:
class Subscriber::VisitController < ApplicationController
def create
# code here
end
end
I have recently changed my database from sqlite3 to Postgres so I could launch it to Heroku. Thus I made all the changes to my database and Gemfile, and lauched. The Heroku app is working well, but when I go back to my local branch I am triggering this error.
ActiveRecord::RecordNotFound in Devise::SessionsController#new
Couldn't find Order with 'id'=2
def current_order
if !session[:order_id].nil?
Order.find(session[:order_id])
else
Order.new
end
Which has me confused because a) Again the Heroku app is working well. and b) Because the conditional clearly states that if order id is nil it should just Make a new order. Why would it freak out if it can't find the id?
As you can see, I am using devise. Here are my cart-relevant models:
class CartsController < ApplicationController
def show
#order_items = current_order.order_items
end
end
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.find(params[:id])
#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(:quantity, :subproduct_id)
end
end
And the models:
class Order < ActiveRecord::Base
belongs_to :order_status
has_many :order_items
before_create :set_order_status
before_save :update_subtotal
def subtotal
order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.unit_price) : 0 }.sum
end
private
# Sets order status of 1, in progress.
def set_order_status
self.order_status_id = 1
end
def update_subtotal
self[:subtotal] = subtotal
end
end
class OrderItem < ActiveRecord::Base
belongs_to :subproduct
belongs_to :order
validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 }
validate :subproduct_present
validate :order_present
before_save :finalize
# Will use the associated subproduct's price if the order item is not persisted
def unit_price
if persisted?
self[:unit_price]
else
subproduct.price
end
end
def total_price
unit_price * quantity
end
private
def subproduct_present
if subproduct.nil?
errors.add(:subproduct, "is not valid or is not active.")
end
end
def order_present
if order.nil?
errors.add(:order, "is not a valid order.")
end
end
def finalize
self[:unit_price] = unit_price
self[:total_price] = quantity * self[:unit_price]
end
end
class OrderStatus < ActiveRecord::Base
has_many :orders
end
Thank you in advance.
Locally, your session still has the order_id key set to 2. That's why your code passes the if-check and gets to the Order.find call. The reason why it blows up is because the default behavior of find is now to raise ActiveRecord::RecordNotFound if it can't find a matching record.
Now, that's probably also the correct behavior for your website. If somehow a session is tied to an order that doesn't exist, you'd be in an inconsistent state! I think you're seeing this now because you rebuilt your database, wiping the order record that your session was tied to, but haven't cleared your session. If you do so (e.g. by deleting all cookies for your localhost domain, or by closing your browser fully and re-opening it), the error will go away.
Couldn't find Order with 'id'=2
The error is because there is no Order record with id = 2 in your local DB and there could be a child record in your order_items table with order_id = 2. So the session[:order_id] won't be nil and it finds the Order record with id = 2 which didn't exist.
The reason could be because the Order record with id = 2 may be accidentally deleted. For a quick fix, try clearing all the cookies or try deleting the order_items record with order_id = 2 and make sure every order_items record have a correct order_id that corresponding to existing order record.
Clear the cookies, as you've been told, that would cause a possible problem. You could also do this. What it does is check if the order found in the cookie is still available, if not, it will reset it then create a new one without throwing an error
def current_order
if !session[:order_id].nil?
#find_order = Order.find(session[:order_id]) rescue nil
if !#find_order
session[:order_id] = nil
current_order
elsif #find_order.order_status_id != 1
session[:order_id] = nil
current_order
else
Order.find(session[:order_id])
end
else
Order.new
end
end
Experimenting with ruby on rails.. I put a new Post form on a users show page.(i.e. 0.0.0.0:3000/users/2) I'm trying to extract the user's id and insert it into a 'user_id' field in the Post table when you create a new post. So when the form is submitted from the user's page, I can link it to the user that wrote it.
models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
before_save :create_user_id
def create_user_id
self.user_id = current_user
end
end
models/user.rb
class User < ActiveRecord::Base
has_many :posts
end
helpers/application_helper.rb
module ApplicationHelper
def current_user
#current_user ||= User.find(params[:id])
end
end
controllers/post_controller.rb
class PostsController < ApplicationController
def new
#post = Post.new
end
def show
#post = Post.find(params[:id])
#page_title = #post.title.capitalize
#author = User.find(#post.user_id)
#author_url = "/users/" + #post.user_id.to_s
end
def create
#post = Post.create(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
# private
private
def post_params
params.require(:post).permit(:title, :body, :user_id)
end
end
The error I get:
Couldn't find User without an ID
Extracted source (around line #15):
#post = Post.find(params[:id])
#page_title = #post.title.capitalize
>>#author = User.find(#post.user_id)
#author_url = "/users/" + #post.user_id.to_s
end
If I test and change my application_helper.rb to this it works, and inserts 2 into the Post's user_id field. The current set up just returns nil
module ApplicationHelper
def current_user
#current_user = 2
end
end
First you want to get the current user, for now you can test using something like this:
#current_user ||= User.find(2)
Note that there will not be an :id param available on a create call, :id refers to a specific member of your resource so in this case if get http://localhost:3000/posts/1 posts would be the resource and 1 would be the param :id so this would not return the current_user you expected.
Then association should do all of the work for you and there is no need for the create_user_id method. All you would have to do is tweak your create method to
#post = current_user.posts.create(post_params)
I trying to create an association between two objects and save the changes to the database.
I have included in the notice a call on the object, to test if it saves after it passes true to the if stament. When I check to see if the update has actually occurred in the data base nothing has changed.
requests_controller.rb
class RequestsController < ApplicationController
before_filter :load_requestable
def accept
#request = Request.find(params[:id])
#request.profile.send("#{#belongs_to}=",#requestable)
if #request.save
redirect_to [#requestable, :requests], notice: "Request Accepted #{#request.profile.send("#{#belongs_to}").name}"
else
render :new
end
end
private
def load_requestable
klass = [Company, Profile].detect { |c| params["#{c.name.underscore}_id"]}
#requestable = klass.find(params["#{klass.name.underscore}_id"])
#belongs_to = klass.to_s.downcase
end
end
Try saving profile directly (since you're modifying it, not the request object)
if #request.profile.save
# redirect