Ruby on Rails collection_select complexity - ruby-on-rails

i have the following Problem, i Have the following in my customer bill view
<%= f.collection_select :product_id,Product.all,:id,:name %>
This is getting list of all the products from "Product" model and giving option to select from it. But i want to select the list of products from the "StoreOpeningStock" model.
I have these in my model
class Product< ActiveRecord::Base
has_many :store_opening_stocks
has_many :customer_bills
attr_accessible :name
end
class StoreOpeningStock < ActiveRecord::Base
attr_accessible :product_id
belongs_to :product
end
class CustomerBill < ActiveRecord::Base
attr_accessible :product_id
belongs_to :product
accepts_nested_attributes_for :store_opening_stock
end
Can anyone guide me how i can get product name and id from store_opening_stock??? Should i use Helpers??? or is there any other way?? Thanks in advance
I tried using helpers
def getting_prod_names
#sto = StoreOpeningStock.all
for x in #sto
[
['{x.product.title}', '{x.product_id}']
]
end
end
getting following output
<%= f.select :product_id, options_for_select(getting_prod_names) %>
ANy Help?? :)

When you create a form the data used to ccreate a collection_select isnt limited to the Class your going to create an object for. You could simply do the following:
<%= f.collection_select :product_id,StoreOpeningStock.all,:product_id ,:name %>
This should to it for you,...
add this to your StoreOpeningStock class:
def name
return self.product.name unless self.product.nil?
""
end

You need to clarify the relationship between your models...
But just to give you an idea. You can define the collection of products you want to display in your controller, inside the action related to the view (where you are displaying the collection).
Controller:
#products= #here you should call all products you want
Then, your collection of products can be displayed like:
<%= f.collection_select :product_id, #products,:id,:name %>
EDIT
You need to revise the relationship between your models. A product has many customer_bills, but are you sure that each customer_bill belongs to a single product?
I think you have a many-to-many relationship, as a customer_bill can also have many products.
If I understand it right, the solution is to create a ProductLine model between this many-to-many relationship.
Also, what is the difference between Product and StoreOpeningStock? What attributes have you included in the StoreOpeningStock?
If you have created this model only to show the availability of products, why don't you add an attribute in the Product model, for example a boolean column called availability.

So you want to find all products that have a StoreOpeningStock.
This is solely a model concern and have nothing to do with helpers.
class Product
# Find all products that have a StoreOpeningStock
def self.in_stock
find(StoreOpeningStock.product_ids)
end
end
class StoreOpeningStock
# Collect all product ids from stocks
def self.product_ids
uniq.pluck(:product_id)
end
end
Now you can use Product.in_stock instead of Product.all to have the only ones in stock.

I'd add a scope to your products model:
class Product< ActiveRecord::Base
has_many :store_opening_stocks
has_many :customer_bills
attr_accessible :name
scope :having_store_opening_stocks, :joins => : store_opening_stocks, :select => 'distinct product.*', :conditions => 'store_opening_stocks.product > 0'
end
Then you can use Product.all.having_store_opening_stocks to select only products with such stocks, for example:
<%= f.select :product_id, Product.having_store_opening_stocks.map { |product| [product.name, product.id] } %>

Related

Ruby on Rails - Bullet/N+1

On Rails 4. I recently installed the bullet gem for my development environment to clear up my app's N+1 queries. Relevant models:
Submissions: Belongs to Categories and Users. Has many SubmissionDetails.
Users: Has many Submissions
Categories: Has many Submissions. Belongs to Awards.
Awards: Has many Categories (and Submissions through Categories)
SubmissionDetails: Belongs to Submissions
In my submission's index page, I have an each do statement to display each submission made by the current user.
<% current_user.submissions.order('created_at DESC').in_groups_of(3, false) do |group| %>
<div class="row">
<% group.each do |submission| %>
After that, I list information about the submission, including its associated category, award, and submission details information. Bullet is saying I'm having N+1 issues with this statement:
N+1 Query detected Submission => [:category] Add to your finder: :include => [:category]
N+1 Query detected Submission => [:user] Add to your finder: :include => [:user]
N+1 Query detected Submission => [:submission_details] Add to your finder: :include => [:submission_details]
Every time I try to add .includes with all three of those models, it only picks the first one I list (this is not surprising). I figure I need to go a different route when multiple models are involved--perhaps a join statement?
(When I make :category the first item, it adds this notice):
N+1 Query detected Category => [:award] Add to your finder: :include => [:award]
(So I also need to include as part of the statement a way to make Award fit in there as well, which, again, has many Submissions through Categories).
So assuming I can't do one .includes for three different models, is there another way to go about this? Thanks.
Just to be more clear, let me make the details more visible:
class User < ActiveRecord::Base
has_many :submissions
end
class Submission < ActiveRecord::Base
belongs_to :category
belongs_to :user
has_many :submission_details
end
class SubmissionDetail < ActiveRecord::Base
belongs_to :submission
end
class Category < ActiveRecord::Base
belongs_to :award
has_many :submissions
end
class Award < ActiveRecord::Base
has_many :categories
has_many :submissions, through: :categories
end
If I understand correctly, for your current_user, you are listing his submissions.
For each submission you want to list submission_details and the category it belongs.
For every category you list the award too.
<% current_user.submissions.order('created_at DESC').in_groups_of(3, false) do |group| %>
<div class="row">
<% group.each do |submission| %>
...
<div><%= submission.category %></div>
<div><%= submission.category.award %></div>
<%= submission.submissions_details.each do |submission_detail| %>
...
<% end %>
<% end %>
</div>
<% end %>
You can remove N+1 problem by using includes in the following manner:
current_user.submissions.includes(:submission_details, :category => :award)
For more details about includes please refer to:
Rails guides eager-loading-associations
Rails guides eager-loading-multiple-associations
Rails api - includes
associations
To include those associations, I would create a scope for submissions. While you're at it, add a latest scope.
class Submission
scope :eager, -> { includes(:submission_details, :category => [:award]) }
scope :latest, -> { order("created_at DESC") }
end
Then simply
current_user.submissions.latest.eager [...]
You shouldn't have to include :user, but I've noticed Rails isn't too clever about such references.

How can I create dynamic form field to store/update hash sets in Rails?

In my reservations table I have a rooms (text) field to store hash values such (1 => 3) where 1 is roomtype and 3 corresponds to the amount of rooms booked by the same agent.
My Reservation model
serialize reserved_rooms, Hash
Here is my nested resource
resources :hotels do
resources :roomtypes, :reservations
end
RoomType stores a single room type which belongs to Hotel model. Though I can enlist roomtypes within my reservation form I do not know how I can create a dynamic hash via form to create/update this hash.
I have this but I am looking for a way to create a dynamic hash "key, value" set. Meaning, if Hotel model has two RoomType my hash would be {12 = > 5, 15 => 1} (keys corresponds to the roomtype_ids while values are the amount}
<%= f.fields_for ([:roomtypes, #hotel]) do |ff| %>
<% #hotel.roomtypes.each do |roomtype| %>
<%= ff.label roomtype.name %>
<%= f.select :reserved_rooms, ((0..50).map {|i| [i,i] }), :include_blank => "" %>
<% end %>
<% end %>
What I want is what this website has in the availability section (nr. of rooms):
specs: rails 4.1, ruby 2.1
Note: If you think there is a design problem with this approach (storing reserved_room in a serialized field) I can follow another path by creating another table to store the data.
Might need tweaking but i used similar code with check-boxes and it worked!
<% #hotel.roomtypes.each do |roomtype| %>
<%= f.label roomtype.name %>
<%= f.select :"reserved_rooms[roomtype.id]", ((0..50).map {|i| [i,i] }), :include_blank => "" %>
<% end %>
This gets messy enough that I would probably consider going with a separate models as you mentioned. I would simply do:
class Hotel < ActiveRecord::Base
has_many :room_types
has_many :rooms, :through => :room_types
end
class RoomType < ActiveRecord::Base
has_many :rooms
end
class Room < ActiveRecord::Base
has_many :reservations
belongs_to :room_type
end
class Reservation < ActiveRecord::Base
belongs_to :room
belongs_to :agent
end
class Agent < ActiveRecord::Base
has_many :reservations
end
Then just use a generic form to submit the # Rooms integer, and let your controller handle making multiple reservations...? Maybe I'm not understanding your objective well enough...
Rails 4 has a new feature called Store you would love. You can easily use it to store a hash set which is not predefined. You can define an accessor for it and it is recommended you declare the database column used for the serialized store as a text, so there's plenty of room. The original example:
class User < ActiveRecord::Base
store :settings, accessors: [ :color, :homepage ], coder: JSON
end
u = User.new(color: 'black', homepage: '37signals.com')
u.color # Accessor stored attribute
u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
# There is no difference between strings and symbols for accessing custom attributes
u.settings[:country] # => 'Denmark'
u.settings['country'] # => 'Denmark'

How to display associated model's attribute in Active Admin index with belongs_to/has_many relationship (Rails 3.2/Active Admin)

I'm building a daily deal Rails app to learn RoR.
I am facing a problem for the past few hours : i can't get a model's attribute of an other associated model on active admin. Let me show you exactly the problem :
I have two models: Brand (i.e the brand of the deal) and Deal. A deal belongs to a Brand but a Brand can have many Deals.
models/deal.rb is like this:
class Deal < ActiveRecord::Base
belongs_to :brand
and we have models/brand.rb:
class Brand < ActiveRecord::Base
has_many :deals
attr_accessible :name
And i did the t.belongs_to in my migrations so this is ok.
In Active Admin's Deals' create form , i type, as admin, which brand the deal is associated with:
admin/game.rb
ActiveAdmin.register Deal do
# -- Form -----------------------------------------------------------
form do |f|
f.inputs "Brand (i.e. client)" do
f.input :brand_id, :label => "Select a brand:", :as => :select, :collection => Brand.all
end
it works great, and i can create Deals with a certain brand.
but I CAN'T manage to display the NAME of the Brand in my list of Deals:
ActiveAdmin.register Deal do
index do
selectable_column
# id_column
column :title
column :deal_amount
column :brand do |deal|
link_to deal.brand.name
end
...doesn't work.
How can I do that ?
I tried everything but i basically don't know how to fetch the name of a Brand given it matches the brand_id in the Deal's table.
Any help appreciated.
show do |f|
panel "Subject" do
attributes_table_for f, :name, :description, :is_visible
end
panel "Pages in List View" do
table_for(f.pages) do |page|
column :name
column :permalink
column :is_visible
end
end
panel "Pages in View " do
div_for(f.pages) do |page|
panel page.name do
attributes_table_for page, :name, :description, :is_visible
end
end
end
end
end
You can do nested relations in same style as parent model
A couple things seem missing:
class Deal < ActiveRecord::Base
belongs_to :brands, foreign_key: :brand_id, class_name: 'Brand'
end
This is assuming that you mean partner to be a Brand and your schema uses brand_id for that relationship.
In your form, you can simply use:
form do |f|
f.inputs "Brand (i.e. client)" do
f.input :partner, label: 'Select a brand:'
end
end
Your link_to call won't actually link to a url the way you have it.
column :brand do |deal|
link_to deal.partner.name, admin_brand_path(deal.partner)
# or simpler
auto_link deal.partner
end
I would highly recommend trying to be consistent in your naming, as it will make things a lot less confusing and will require less code to make things work. i.e.
class Deal < ActiveRecord::Base
belongs_to :brand
end
f.input :brand, label: 'Select a brand:'
auto_link deal.brand
And your DB column can still be named brand_id.

Ransackable Attributes for has many relationship columns

I want to display has_many relationship columns in ransackable attributes list. So that I can display them in the dropdown.
I have a member model
class Member < ActiveRecord::Base
has_many :memberships
def self.ransackable_attributes(auth_object = nil)
if auth_object == 'admin'
super
else
super & ['first_name', 'last_name', 'license_number', 'memberships_membership_number_cont']
end
end
And membership model has some columns like membership_number which is unique and a string. Now in the dropdown of members listing page I want to provide membership_number, so that user can select membership_number from the dropdown and enter a value to search the respective member.
Any suggestions?
The dropdown I am taking about is:
PS: In the screenshot you may be looking for a dropdown for contains all/contain any ie options dropdown. I made is just one only contains any. Thats why its not visible.
You need to define the ransackable_attributes method in associated model for custom searchable attributes of that model. So your Membership model should be something like:
class Membership < ActiveRecord::Base
belongs_to :member
...
def self.ransackable_attributes(auth_object = nil)
['membership_number', ...]
end
end
And specify associations in ranssack form like:
<%= f.condition_fields do |c| %>
<%= c.attribute_fields do |a| %>
<%= a.attribute_select associations: [:memberships] %>
<% end %>
<% end %>

Problems Using collection_select to store value in the database

I have two models:
Project.rb
class Project < ActiveRecord::Base
belongs_to :customer
end
and Customer.rb
class Customer < ActiveRecord::Base
has_many :projects
end
Inside the _form.html.erb I have:
<p>
<label>Select Customer</label>
<%= f.collection_select :customer_id, Customer.all, :id, :name, :include_blank => true %>
</p>
Which should Collect the Customers from the Customer model and display all the customers, finally it should assign the value to the customer_id which is in projects table.
Rite now the everything is passing when i check the log. When I select the first customer with value=1, it passes customer_id = "1" in my log but it doesn't get stored in the table. It shows customer_id = nil in the projects table.
Can someone help. Thanks :)
Do check that you added customer_id in attr_accessible method like,
class Project
attr_accessible :your_other_attributes, :customer_id
end

Resources