How can I reference an object with a dropdown input in Rails? - ruby-on-rails

I have models Order and Vendor. Order has vendor, description, items, cost fields, and Vendor has name, email, phone.
When a user creates a new Order, I would like them to select the Vendor's name from a dropdown input, which references the whole relevant Vendor objects in the Order's vendor field.

As per my understanding. you have following associations
Vendor has_many Orders
Order belongs_to Vendor [In order we need to store vendor_id]
So in that case in your new order form you need to add
<%= f.select(:Vendor_id, Vendor.all.collect { |v| [ v.name, v.id ] }, {}, { "data-placeholder": "Select Vendor" }) %>
This will give you selected vender_id in parameters. Hope this will work for you.
Thanks!

Related

Sort by columns in both tables when using "includes" clause in Ruby on Rails

Account.includes(:month_forecasts)
.where(month_forecasts: { field: :value })
I have the above includes clause. I have 2 fields F1 in Account table and F2 in MonthForecast table. I need to sort the above result by both these fields.
Account.includes(:month_forecasts)
.where(month_forecasts: { field: :value }).order(:f1)
I know the above would sort by F1 but since F2 is in the MonthForecast table, I am not sure how to sort by that field.
I would use joins to make this association, if the association with month_forecasts is not for all data, then it will only have the relevant results links. Also leave the fields described in the order will help in the next reading.
Account.joins(:month_forecasts).where(
month_forecasts: { field: :value }).order('accounts.f1, month_forecasts.f2')
Just use field names as string
Account.left_outer_joins(:month_forecasts)
.where(month_forecasts: { field: :value }).order('f1, month_forecasts.f2')
Note, you need to use left_outer_joins (or joins if you don't want to see accounts without forecasts) instead of includes for sorting
UPDATE:
By default it selects only accounts.*, but you can add a select with all columns from the second table
Account.left_outer_joins(:month_forecasts).select('accounts.*, month_forecasts.*')
.where(month_forecasts: { field: :value }).order('f1, month_forecasts.f2')

ActiveRecord includes doesn't include all parent records?

I would like to get all customers and their products that are under category 'Beverages'.
Here are my relations:
Customer has_many orders
Order has_many products through order_details
Product has_many Orders through order_details
Product belongs to Category
I tried this but it doesn't give all customers. It gives only customers who order Beverages.
Customer.includes(orders: {products: :category}).where(categories: { category_name: "Beverages"})
Note: I don't want to filter Customer records. I need to apply filter on products because loading all records are useless in this case.
Active Record doesn't have an API to do this, unfortunately.
If you only need a few known categories, you can do it by adding a custom association:
class Customer ..
has_many :beverage_orders, -> { includes(products: :categories).where(categories: { category_name: "Beverages" }) }, class_name: "Order"
(and then Customer.includes(:beverage_orders))
If you need to support arbitrary categories, your options get even worse... if there are few enough customers, I'd consider just skipping the include and accepting the N+1 query.
Otherwise, the next least bad option I can suggest is to do the query yourself:
customers = Customer.all # or whatever
beverage_orders = Order.includes(products: :categories).
where(categories: { category_name: "Beverages" }).
where(customer_id: customers).
group_by(&:customer_id)
customers.each do |customer|
puts "#{customer.name} ordered #{beverage_orders[customer.id].size} beverages"
# i.e., use `beverage_orders[customer.id]` instead of `customer.orders`
end
If you need to filter your records, why you are using left join?
Try
Customer.joins(:orders).where(order: { product_id: Category.find_by(name: 'Beverages').products.pluck(:id) })
UPDATE:
As where clause will filter the base record, so it should filter your Customer model you need to customize joins to select proper products instead of filtering customers.
category_id = Category.find_by(name: "Beverages").id
Customer.joins(orders).joins("LEFT OUTER JOIN products ON products.id = orders.product_id AND products.category_id = #{category_id}")

Rails/Postgresql: Foreign keys stored in an array not returning all associated rows

I have two models
Product { id, name, tags_id[] }
Tag { id, name }
tags_id is an array that stores the ids of Tags.
class Product < ActiveRecord::Base
has_many :tags, foreign_key: 'id'
end
Let's say I have a product with two tags(1,2)
When I query for the product, it only loads one tag, the first one.
Product.includes(:tags).all
Here is how the query looks like:
SELECT "tags".* FROM "tags" WHERE "tags"."id" IN (1)
Is there an option I need to pass to has_many of Product to receive both the tags? Is this even possible?
Thanks for the help.
Try tag_ids, not tags_id column.
As I know it, you can't do that. Just place standard product_id column for tags, and remove tag_ids column.
Your current setup would not work.
You need to check has_array_of gem from here.

See if one person is before another in the alphabet, ruby, rails

I'm doing an app for a membership database.
Each person may have a partner. When it comes to displaying the list, I only want to have one row for each family, so at the moment I'm comparing first names and not displaying the row if the person's name is second. Like this
person.first_name != [person.first_name, person.partner.first_name].sort[0]
This means each family only gets displayed once, not twice - once for each partner.
And I'm doing this in the view.
There must be a better way of doing this, and it'd be really great if I could do it at the database level. I'm using postgresql if that makes a difference.
Edit
Sorry if it was unclear.
Say Person 1 has the first_name "Edward" and Person 2 has the first_name "Fay". Edward and Fay are married.
I only want to show them once in my list - I want a row to look like this
Surname First name Address etc
Mysurname Edward ....
Fay
I don't want to display it again with Fay first because I've got both Fay and Edward in list of people, so I use the ruby in the first part of the question to check if I should display the row - it compares their first names and only does the row if the person has a fist name that's before his/her partner's first name.
Here's the relevant part of my person model
class Person < ActiveRecord::Base
has_one :relationship_link, :foreign_key => :person_id, :dependent => :destroy, :include => :partner
has_one :partner, :through => :relationship_link, :source => :person_b, :class_name => "Person"
I hope that's clearer
You need to use DISTINCT ON or GROUP BY. In postgres you need to be careful to group by everything that you are selecting. If you only need to get the last names you can select("DISTINCT ON(last_name) last_name").pluck("last_name"). You will only get an array of last names though.
Maybe you can get records if you order by every other fields in your table, like this:
select("DISTINCT ON(people.last_name) people.*").order("people.last_name ASC, people.first_name ASC, people.field2 DESC, people.field3 ASC...")
You need to order by every attribute so the result is not ambigious.
For this case, i would create a data structure (a Hash) to store people instances given a specific surname. Something like this:
def build_surnames_hash(people_array)
surnames_hash = {}
people_array.each do |person|
last_name = person.last_name
surnames_hash[last_name] ||= []
surnames_hash[last_name] << person
end
surnames_hash
end
That way, you can iterate over the hash and display people using their surnames stored as hash's keys:
surnames_hash = build_surnames_hash(Person.all)
surnames_hash.each do |surname, person_instances_array|
# display the surname once
# iterate over person_instances_array displaying their properties
end

IF/CASE statement OR MySQL OR array for get category name

On my site I got entries which have category. Site have only 5 categories, so I have dilemma:
Make relationship between category table and entries (category_id) table
OR
Make method which return category name via IF/CASE statement? Like:
case #entry.category.id
when 1
"Games"
when 2
"Movies"
when 3
"Fun"
[...]
end
(I remind that I must get 10 category name per page)
OR
Use array:
cat[1] = "Games"
cat[2] = "Movies"
cat[3] = "Fun"
[...]
<%= cat[#entry.category.id] %>
I think this relation definitely belongs into the database. (adding a category table)
it is the most sane and most scalable option.
It is also the cleanest, because you break the seperation of data, display and logic (MVC: model, view, controller) when hardcoding the categories in your application.
you can easily select the item AND its category with a single query:
SELECT item.*, category.name
FROM item
LEFT JOIN category ON category.id = item.category_id
WHERE some=condition
there are similar queries for INSERTs and UPDATEs (at least in MySQL), so you never need a second query.
If the only thing you care about category is "name", then you should just store the category_name in the entries table.
OR
Make a constant CATEGORY_NAME and wrapper method to get the name with id in the entries table (without using Category table/model at all). eg.,
class Entry
CATEGORY_NAME = [ "Games", "Movies", "Fun"]
def category_name
CATEGORY_NAME[cat_id] #cat_id being just 0,1,2 .. depends how you want to store
end
...
I am sure there are many ways to achieve this anyway.
Hope it helps.

Resources