Create/Edit middle model class in many-to-many relationship - ruby-on-rails

Currently, my app have 3 models,
I want to add new Receipt by specifying the quantity of food required.
class Receipt < ActiveRecord::Base
# columns: id, place
has_many :receipt_foods
has_many :foods, through: :receipt_food
end
class ReceiptFood < ActiveRecord::Base
# columns: id, quantity, receipt_id, food_id
belongs_to :receipt
belongs_to :food
end
class Food < ActiveRecord::Base
# columns: id, name
has_many :receipt_foods
has_many :receipts, through: :receipt_food
end
My problem is how to create the quantity of food in my form,
for example: create 3 different food where 3 quantity each food
I know how to create this in rails console only, don't know how in web using form_for
I have try fields_for but most examples explain create and edit 2 models property only.
Can any one suggest ways or any materials/articles talk about this?
Thanks

I am not sure, but I would do:
= form_for :receipt_food do |f|
= f.number_field :quantity, min: 0
= f.select :food_id, Food.all.map{ |food| [food.name, food.id] }
= f.select :receipt_id, Receipt.all.map{ |receipt| [receipt.place, receipt.id] }
I would add labels to these fields.
Also, I think the convention is to name it: FoodReceipt, it's alphabetical.

Related

Edit relationship of parent record in activeadmin

I have the following model structure:
Composition has many Score (Score belongs to Composition)
Composition has and belongs to many Countries (and viceversa)
score.rb:
class Score < ApplicationRecord
belongs_to :composition
end
composition.rb:
class Composition < ApplicationRecord
has_many :scores
has_and_belongs_to_many :countries, join_table: :rights_countries
end
country.rb:
class Country < ApplicationRecord
has_and_belongs_to_many :compositions, join_table: :rights_countries
end
In activeadmin, I want to be able to edit the countries of a composition, but in the edit form of its scores.
Of course, the form will import this data from composition, and default inputs will be equal for all the scores (children) of a composition.
I found no way to implement this in activeadmin up to now.
Is this even possible? If yes, is the solution easy or cumbersome?
Following this link, I added an inputs within an inputs and updated the corresponding params. I also added accepts_nested_attributes_for :composition in the score model.
app/models/score.rb
...
accepts_nested_attributes_for :composition
...
app/admin/score.rb
...
permit_params ...,
composition_attributes: [:id, country_ids: []]
...
form do |f|
f.inputs do
...
f.inputs "", for: [:composition, score.composition] do |c|
c.input :countries, as: :select, collection: Country.order_by_name.uniq.map { |p| [p.name, p.id] }
end
end
end
Let me know if there's a cleaner solution.

Allow extra parameter for has_many through join table with Rails 4

My models are setup as follows:
class Contract <ActiveRecord::Base
has_many :contract_upgrades
has_many :upgrades, through: :contract_upgrades
end
My Upgrade model
class Upgrade < ActiveRecord::Base
has_many :contract_upgrades
has_many :contracts, through: :contract_upgrades
end
my ContractUpgrade model
# schema
t.integer :contract_id
t.integer :upgrade_id
t.integer :qty
class ContractUpgrade < ActiveRecord::Base
belongs_to :contract
belongs_to :upgrade
end
My contract form looks like this...
- #upgrades.available.each do |upgrade|
%tr{class: "#{upgrade.id}", id: 'upgrade-checkboxes'}
%td
.upgrade-checkboxes
= check_box_tag "contract[upgrade_ids][]", upgrade.id, #contract.upgrades.include?(upgrade), { upgrade_id: upgrade.id }
= label_tag "contract_upgrade_#{upgrade.id}", upgrade.name
%td
= number_to_currency("#{upgrade.price}")
- if upgrade.allow_each
here is where it gets a little tricky...
%td
%input{type: 'text', placeholder: 'Qty'} # displays text_field box
= number_field_tag # displays text_field box
When the upgrade check_box is checked the upgrade.id and contract.id save as it should and works perfectly. I need to be able to input a quantity on those upgrades that can have multiple. In my Contracts controller the upgrade_ids are whitelisted via {upgrade_ids: []}, and no matter what I've done whenever anything is in the qty field I get an unpermitted_parameter :qty...
UPDATE
I'd be able to figure out what/how to do what I essentially need to do if someone can get Rails Guides Associations: has_many_through example to work. In this example directly from the rails guides the join table has an appointment_date attribute.

return the count for element in a has_many relationships

i have 2 models one is listing and user
user has_many listings
listing belongs_to user
i have a view setup , i want to display for each user their own listings count ,i try this code :
<% User.all.each do |user| %>
<%= user.listings.count %>
<% end %>
i want to grab the listing count for each user . i found a bunch of solution here , all return the loop .other solutions i tried is to create a class method .
def count_listings
Listing.where(:user_id => user.id).count
end
try to call this way <%= User.count_listings%> it doesn't work .
for some reason there something i'm missing ,can't quite figure it out .
The :counter_cache option can be used to make finding the number of belonging objects more efficient. Consider these models:
class Order < ActiveRecord::Base
belongs_to :customer
end
class Customer < ActiveRecord::Base
has_many :orders
end
With these declarations, asking for the value of #customer.orders.size requires making a call to the database to perform a COUNT(*) query. To avoid this call, you can add a counter cache to the belonging model:
class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: true
end
class Customer < ActiveRecord::Base
has_many :orders
end
With this declaration, Rails will keep the cache value up to date, and then return that value in response to the size method.
Although the :counter_cache option is specified on the model that includes the belongs_to declaration, the actual column must be added to the associated model. In the case above, you would need to add a column named orders_count to the Customer model. You can override the default column name if you need to:
class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: :count_of_orders
end
class Customer < ActiveRecord::Base
has_many :orders
end
Counter cache columns are added to the containing model's list of read-only attributes through attr_readonly.
source: Rails guide on associations
..scroll down to options of belongs_to
If all you need is what you show in the example you can do it better as follows
<% Listing.group(:user_id).count.each do |user, count| %>
<%= "user: #{user} has #{count} listings" %>
<% end %>
This does a single query to the database and fetches only what you need.
SELECT COUNT(*) AS count_all, user_id AS user_id FROM `listings` GROUP BY user_id
and returns a hash like:
{
1: 123,
2: 231
}
#{ user_id: count }

rails 4 associated models with class_name in select, shows all. Some advise needed

As I am learning RoR now, I would like to know a more appropriated (rails) way to achieve that the application only shows associated resources.
Right now I have the following models:
class Account < ActiveRecord::Base
has_many :billing_accounts
has_many :addresses
end
class BillingAccount < ActiveRecord::Base
belongs_to :invoice_address,
class_name: "Address",
foreign_key:"invoice_address_id"
end
class Address < ActiveRecord::Base
has_many :billing_accounts
belongs_to :account
end
In my edit.billing_account I have this form:
= simple_form_for([:account, #billing_account]) do |f|
= f.association :invoice_address
I expected that only the associated address will be shwon, but this shows "all" address records in the database (also from other user accounts).
Users only should be able to see account.addresses and for now I do this with:
= f.association :invoice_address, collection: current_user.account.addresses.all
But I am sure there is better way to do this inside the models. For every form I now use current_user.account.MODEL.all but that is not very DRY I think.
So basically what I want is only to use =f.association :invoice_address and BillingAccount should know it only can show the account.addresses.
Suggestions are welcome. Thanks!
You just need to set default_scope for nested models:
class Address < ActiveRecord::Base
default_scope { where(account_id: current_user.account_id) }
But in this case you should define current_user in models
In your case you should use f.simple_fields_for instead of f.association as described here: https://github.com/plataformatec/simple_form/wiki/Nested-Models
class BillingAccount < ActiveRecord::Base
belongs_to :invoice_address,
class_name: "Address",
foreign_key:"invoice_address_id"
accepts_nested_attributes_for :invoice_address
end
View:
= simple_form_for([:account, #billing_account]) do |f|
= f.simple_fields_for :invoice_address do |f_address|
= f_address.input :street
= f_address.input :zipcode
...
Don't forget to build invoice_address of account in a controller if it is needed. For example:
class BillingAccountController < ApplicationController
def new
#billing_account = BillingAccount.new
#billing_account.build_invoice_address
end
Since you're using has_many you can use the plural version of the model name rather than current_user.account.MODEL.all.
Like this:
current_user.account.addresses
or
current_user.account.billing_accounts
It even works the other way with belongs_to:
#address = Address.last
#address.accounts
Try to add conditions to belongs_to association:
class BillingAccount < ActiveRecord::Base
belongs_to :invoice_address,
->(billing_account) { where "account_id = #{billing_account.account_id}" },
class_name: "Address",
foreign_key:"invoice_address_id"
end

Ruby on Rails collection_select complexity

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] } %>

Resources