I am trying to write some logic that will evaluate if an item already exists in a basket and if it does increment the items quantity by 1 when the user adds the product and if not create a new record (the creating a new record bit is working well).
def create
#product = Product.find(params[:product_id])
#basket = current_basket
if #basket.items.exists?(product_id: #product.id)
current_basket.items.find(conditions: {:product_id => #product.id}).increment! :quantity
else
Item.create!(basket_id: #basket.id, product_id: #product.id, quantity: 1, price: #product.price)
end
redirect_to baskets_show_path
end
The error I am getting is SQLite3::SQLException: no such column: id.conditions: SELECT "items".* FROM "items" WHERE "items"."basket_id" = ? AND "id"."conditions" = '--- :product_id: 2 ' LIMIT 1
Any help would be much appreciated.
Try using find_by instead of conditions:
def create
#product = Product.find(params[:product_id])
#basket = current_basket
if #basket.items.exists?(product_id: #product.id)
current_basket.items.find_by(product_id: #product.id).increment! :quantity
else
Item.create!(basket_id: #basket.id, product_id: #product.id, quantity: 1, price: #product.price)
end
redirect_to baskets_show_path
end
first_or_create might be helpful. See API Dock ActiveRecord::Relation first_or_create. Of course, your needs are more complex than what is provided in the doc since the item has multiple identifying criteria.
I tested this out with a model in the app I have open and it seemed to do the trick (the model has a lot of validations I didn't want to mess with so I believe the actual create failed on that).
def create
#product = Product.find(params[:product_id])
#basket = current_basket
item = Item.where({basket_id: #basket.id,
product_id: #product.id,
price: #product.price})
.first_or_create(quantity: 0)
item.increment! :quantity
redirect_to baskets_show_path
end
So basically what's going on is, you set item to the item in the basket if it's there, or create it if it's not with the info you were already seeking, as well as an initial quantity of zero. Then, you increment by 1.
One other note is that you may want to confirm you need both instance variables. If just #basket is needed in the view, consider dropping the # from all the product references. An explanation of why and how to keep controllers skinny is in Jumpstart Lab's Slimming Controllers.
Related
I am trying to modify Sharetribe, a Ruby on Rails framework for online communities. There is this method that returns me relevant search filters.
For now, it returns me a filter if it is present in any one of the categories (identified by category_ids ) .
I would like it to return a filter if and only if it is present in ALL of the categories identified by category_ids.
Being new to Rails and ActiveRecord, I'm a bit lost. Here is the method returning relevant filters :
# Database select for "relevant" filters based on the `category_ids`
#
# If `category_ids` is present, returns only filter that belong to
# one of the given categories. Otherwise returns all filters.
#
def select_relevant_filters(category_ids)
relevant_filters =
if category_ids.present?
#current_community
.custom_fields
.joins(:category_custom_fields)
.where("category_custom_fields.category_id": category_ids, search_filter: true)
.distinct
else
#current_community
.custom_fields.where(search_filter: true)
end
relevant_filters.sort
end
Is there a way to change the SQL request, or should I retrieve all the fields as it is doing right now and then delete the ones I am not interested in ?
Try the following
def select_relevant_filters_if_all(category_ids)
relevant_filters =
if category_ids.present?
#current_community
.custom_fields
.joins(:category_custom_fields)
.where("category_custom_fields.category_id": category_ids, search_filter: true)
.group("category_custom_fields.id")
.having("count(category_custom_fields.id)=?", category_ids.count)
.distinct
else
#current_community
.custom_fields.where(search_filter: true)
end
relevant_filters.sort
end
This is a new method in your HomeController, pay attention the name is different, just to omit monkeypatching. Comments are welcome.
So I solved my problem by selecting filters that are pesent in all of the subcategories of the selected category. For that I select all filters of all subcategory, and only keep the ones that are returned a number of times exactly equal to the number of subcategory.
all_relevant_filters = select_relevant_filters(m_selected_category.own_and_subcategory_ids.or_nil)
nb_sub_category = m_selected_category.subcategory_ids.size
if nb_sub_category.none?
relevant_filters = all_relevant_filters
else
relevant_filters = all_relevant_filters.select{ |e| all_relevant_filters.count(e) == nb_sub_category.get }.uniq
end
Is there a way to dynamically add X number of nested form fields? For example if we have a select menu:
Select Menu
-1
-2
-3
-4
And the user selects 3, then create 3 nested form fields.
I have watched the Railscast on Nested model form but to me this already has the one set of fields_for already created and simply inserts them each time the link is clicked. I would like to dynamically insert X amount each time the select menu changes.
Here is some code from the Railscast:
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
sorry for the late answer, but I was just looking around for the same thing. Had you checked this gem?
https://github.com/nathanvda/cocoon
In the show view of my products am using the following function to link to the next product (ordered by price):
class Product < ActiveRecord::Base
belongs_to :user
def next
Product.where("user_id = ? AND price > ?", user_id, price).order("price ASC").first
end
end
This function works as long as all products differ in price. The moment two products have the same price though, the function no longer works because it is always looking for a higher price.
How can I make it that a user can click through all his products using the function above, even when some products do not differ in price?
I tried replacing > with >= but that will always return the same one product, leaving the function essentially useless.
Can anybody help?
This method can only work if the products are strictly ordered. A solution could be to sort them by price first and next by id (the condition a bit more complex also):
def next
Product.where("user_id = :user_id AND (price > :price OR (price = :price AND id > :id))", user_id: user_id, price: price, id: id)
.order("price ASC, id ASC").first
end
If the products have a unique name or label, you could use this attribute instead on the id, so the products with the same price are sorted alphabetically, that could make more sense then the id.
Greater than >= plus different product_id
def next
Product.
where("id <> ?", id).
where("user_id = ?", user_id).
where("price >= ?", price).
order("price ASC").
first
end
I have a set of data that is in two different tables. I want to join them up on my /show page in my ruby on rails app. In the controller, I have it set to find the date, set a variable, use that date to look through the other database to pull the needed information.
My controller looks like this:
def show
#ticket = Ticket.find(params[:id])
#hellodate = Ticket.select(:date)
#winnings = Winnings.find(:all, :conditions => {:date => #hellodate})
respond_to do |format|
format.html # show.html.erb
format.json { render json: #ticket }
end
end
for my #winnings to work, I need it to pull the row that has the date that matches with the #ticket date. I am new to this world and would love any input / solutions because this isn't working. It should only show one #winnings but it shows multiple though only ONE date will eve match. Thanks!
Your #hellodate is not what you think it is. This:
#hellodate = Ticket.select(:date)
will, more or less, give you the result of saying:
select "date" from "tickets"
so you'll get all Tickets but only the date columns will be pulled out of the database. Presumably you just want the date from #ticket:
#ticket = Ticket.find(params[:id])
#winnings = Winnings.where(:date => #ticket.date)
I'm using rails v3.0.9
I'm not able to figure out how to use find method on active record collection
What i tried in my console,
#customers = Customer.all # returns collection of records
#customer = #customers.find {|customer| customer.id == 1 } # returns the correct record
Now i'm using the same find method in association collection
#projects = #customer.projects # returns collection of Project records
#project = #projects.find {|project| project.id == 1 } # got error
ActiveRecord::RecordNotFound: Couldn't find Project with ID=1...
Please anyone explain how does the find method differs from above two examples
Is there any other way to find a record in association collection?
I used Array detect to get my Project record
#project = #projects.detect {|project| project.id == 1 } # returns the correct record
Which method is best to find single record from array of active records?
Simply do this:
#customer = Customer.find(customer_id)
For example:
#customer = Customer.find(1)
# => will return the customer with the id equal to 1
You have a few opportunities:
# this returns the customer with the id 1 - if a customer with id 1 exists!
#customer = Customer.find(1)
#customer = Customer.where(:id => 1).first
# this returns the project with id 1 and customer_id = #customer.id
#customer.projects.find(1)
#customer.projects.where(:id => 1).first
Your error simple means, that you do not have a project with id = 1 and customer_id = #customer.id
To answer your question..you should use the where queries..that's the new rails 3 way. All find methods are deprecated in rails 4 and extracted out of the rails core into an extra gem.