I have a Form_for for a Show model. I would like to use a fields_for within the form_for to add bands. The thing is I don't want the fields tied to the bands when using the form to update records. If the name of the band changes I would like to update the performance with the new band.
Shows are joined with Bands through Performances
class Show < ActiveRecord::Base
has_many :performances
has_many :bands, through: :performances
accepts_nested_attributes_for :bands
end
class Band < ActiveRecord::Base
attr_accessible :name, :website, :country, :state
has_many :performances
has_many :shows, through: :performances
validates :name, presence: true, uniqueness: true
end
class Performance < ActiveRecord::Base
attr_accessible :show, :band
belongs_to :show
belongs_to :band
end
Here is my form. (simplified)
<%= form_for #show do |f| %>
#fields
<%= f.fields_for :bands do |b| %>
<%= b.text_field :name %>
<% end %>
<%end>
The problem is if this is used to change a bands name, it changes the bands name (crazy right?). I don't want it to update the Band record-- I want it to do a Band.find_or_create and update the performance record with the new band's id. This way users can replace a band in a show by removing the name and adding in another band name.
The rendered html should include the performance id not the band id (I think)
Something like:
<input id="show_performance_attributes_1_id" name="show[performance_attributes][1][id]" type="hidden" value="62">
How is this done?
Ok, so I was able to find the solution to my own question. I may have not provided enough details in the original question.
But the solution is simply to use the performance model as the nested field in the Fields_for instead of the bands model. Change the show model to accepts_nested_attributes_for performances and change the performance model to accepts_nested_attributes_for band
Related
I am working on a Rails app where users can post Quotes about different art forms (e.g. Music, Painting, Photography, etc.). Each Quote must be assigned a Medium (such as Music or Photography) and a Genre (such as Jazz or Rock and Roll for music or Landscape for photography).
I am using a grouped_collection_select for the Genre field on my Quote form, and that works great to sort the genres, but I'd like to prevent anyone from selecting a genre that doesn't belong to the medium they have selected.
I know that I can do this dynamically with javascript, as in this Railscast, but I'd like to create a validation to ensure that no bad data gets into the database.
Is there a way to validate this in my Quote model so that there's no way a Quote can be saved with a genre that does not have the correct medium? This would prevent someone from saving something that is the medium "Photography" and the genre "Jazz", for instance.
Here are my models:
class Quote < ApplicationRecord
belongs_to :medium
belongs_to :genre
end
class Medium < ApplicationRecord
has_many :quotes
has_many :genres
end
class Genre < ApplicationRecord
has_many :quotes
belongs_to :medium
end
And here are the fields on my Quote form:
<%= f.label :medium, 'Medium' %>
<%= f.collection_select :medium_id, Medium.order(:name), :id, :name, {prompt: 'Select a medium'}, {class: 'form-control'} %>
<%= f.label :genre, 'Genre' %>
<%= f.grouped_collection_select :genre_id, Medium.order(:name), :genres, :name, :id, :name, {prompt: 'Select a genre'}, {class: 'form-control'} %>
You can use rails validate method to achieve this,
validate: check_medium
def check_medium
errors.add(:base, "Your error message") if genre.try(:medium) != medium
end
My proposal here is to story only genre_id in quotes table. This belongs_to :medium is unnecessary because each genre already knows its medium. With architecture like this, you don't have to worry about medium-genre mismatch at all. Models:
class Quote
belongs_to :genre
# With delegation you still can do something like quote.medium
delegate :medium, to: :genre
end
class Medium
has_many :genres
has_many :quotes, through: :genres
end
class Genre
belongs_to :medium
has_many :quotes
end
I am stuck struggling with rich join tables in Rails 5 and need help getting on track. The app I'm writing will help me track which of our company's suppliers carry which brands of products. Because I also need to track whether each supplier is authorized or unauthorized for each of the brands they sell, and whether they carry those brands in stock, I thought the best approach was to use a join table and store the attributes there. In other words:
Suppliers <---> Lines <---> Brands
Beyond the foreign key references for a Supplier and a Brand, the Line record also has two boolean attributes: .is_authorized and .carries_stock.
My models:
/models/supplier.rb
class Supplier < ApplicationRecord
has_many :lines, :dependent => :destroy
has_many :brands, :through => :lines
accepts_nested_attributes_for :lines
end
/models/brand.rb
class Brand < ApplicationRecord
has_many :lines, :dependent => :destroy
has_many :suppliers, :through => :lines
end
/models/line.rb
class Line < ApplicationRecord
belongs_to :supplier
belongs_to :brand
validates_presence_of :supplier
validates_presence_of :brand
end
I've been able to set up the controller and supplier edit form to allow creating records in the Lines table, but have no clue how to allow the users to edit the .is_authorized and .carries_stock attributes. I have been able to get the create/edit supplier form to work by adding the following snippet:
/views/suppliers/_form.html.erb
<h4>Brands</h4>
<%= form.collection_check_boxes(:brand_ids, Brand.all, :id, :name) do |b| %>
<%= b.label class:"label-checkbox" do%>
<%= b.check_box + b.text%>
<%end%>
<br />
<% end %>
The form looks like this now but doesn't allow me to edit the rich attributes .is_authorized and .carries_stock. I'd like the form to look something more like this. Where do I go from here?
Thanks!!!
I have a common many to many relationship, these are the models:
class Employee < ApplicationRecord
has_many :related_professions
has_many :professions, through: :related_professions
accepts_nested_attributes_for :related_professions
end
class RelatedProfession < ApplicationRecord
belongs_to :employee
belongs_to :profession
accepts_nested_attributes_for :profession
end
class Profession < ApplicationRecord
has_many :related_professions
has_many :employees ,through: :related_professions
end
I also have a form for saving Employees. In this form I would like to render all the Professions in a multiple select for the user to choose as needed. I want that when the user submits the form, the IDs of all the selected professions be saved in the RelatedProfession pivot table (which just have three columns: id, employee_id, profession_id). This is the part of my form for the select:
<div class="field">
<%= form.label :professions %>
<%= form.fields_for :related_professions do |rp| %>
<%= rp.collection_select :profession_id, Profession.all, :id, :name, {}, {multiple: true} %>
<% end %>
</div>
And this is the part in my EmployeeController that allows the parameters:
# Never trust parameters from the scary internet, only allow the white list through.
def employee_params
params.require(:employee).permit(:name, related_professions_attributes: [:id, profession_id: [:id]])
end
The first problem is that the form does not load the Professions if the Employee does not have any assigned. I had to manually add one to the DB and then it would populate the select.
Second problem is that when I try to update the Employee (and also the RelatedProfession pivot table) by selecting a different Profession, it won't work, and I get this error:
Related professions profession must exist
I know there must be something wrong in the permit parameters and form that is not building the select correctly.
I appreciate the help. Thanks in advance.
You no need nested attributes to created has_many through relations,
You can just pass it as array of ids.
class Employee < ApplicationRecord
has_many :related_professions
has_many :professions, through: :related_professions
end
class RelatedProfession < ApplicationRecord
belongs_to :employee
belongs_to :profession
end
class Profession < ApplicationRecord
has_many :related_professions
has_many :employees ,through: :related_professions
end
In form also just select ids of Professions.
<div class="field">
<%= form.label :professions %>
<%= rp.collection_select :profession_ids, Profession.all, :id, :name, {}, {multiple: true} %>
</div>
change strong params to allow profession_ids as array.
def employee_params
params.require(:employee).permit(:name, profession_ids: [])
end
Hope this solves your problem.
I have a project-specific question. I'm trying to determine the most efficient and logical way of setting up my models in my app. The relevant players to model here are: Vendors, Vendor Inventories, and Products. Here is a breakdown of what each item should be able to return in some way:
Inventories:
Store ID
Products and associated details (Price, Name, Brand, Details, Product Code)
Vendors: Store ID, Location, Name
Products: Price, Name, Brand, Details, Product Code
Obviously there is a lot of duplication in this scheme between products and inventories. My issue is that while vendors might have similar items in their inventories, the price will always be different. So I can't simply relate the models by product code. Because most vendors will have the same products, if I were to model store inventories with all of the product information, wouldn't that be a lot of duplication? It's also possible that I don't need to have a separate model for vendors and could just try and keep it all inventories, but I'm lost. Help please?! Thanks in advance.
EDIT:
Here is my model structure, though I'm not sure it's ideal.
class Vendor < ActiveRecord::Base
attr_accessible :name, :address
has_one :inventory
has_many :products, through: :inventories
end
class Inventory < ActiveRecord::Base
has_many :products
belongs_to :vendor
end
class Product < ActiveRecord::Base
attr_accessible :upc, :brand, :product, :details, :price
has_many :inventories
has_many :vendors, through: :inventories
end
I'm not sure if I understand your specific needs, especially if the set of models are just for example, but judging from the names, here would be my implementation.
A Vendor is the main entity, it will own many products through the join table inventory,
class Vendor < ActiveRecord::Base
attr_accessible :name, :address
has_many :inventories
has_many :products, through: :inventories
end
However there are aspects of the product that need to differ between Vendors, in this case, price and quantity( I just made that up), which can be stored in the join table, since it would be independent and made for each product and vendor relationship, is ideal for storing info like price and quantity.
class Inventory < ActiveRecord::Base
attr_accessible :product_id, :vendor_id, :price, :quantity
belongs_to :product
belongs_to :vendor
end
Product should only have attributes that would be universal regardless how the vendor handles it, likewise if the product where to be changed, it should be okay with all the vendors.
class Product < ActiveRecord::Base
attr_accessible :upc, :brand, :product, :details
has_many :inventories
has_many :vendors, through: :inventories
end
Doing this you may have difficulties editing this in the view, setting prices and quantity on inventory. Again, my way of doing it would be to use nested fields and interact directly with the join table, and not products, here's some quick partial code using simple_form gem,
on you form,
<%= f.simple_fields_for :inventories do |inventory_fields| %>
<%= render partial: "inventory_fields", locals: {f: inventory_fields }%>
<% end %>
<%= link_to_add_fields "Add", f, :f %>
and in inventory_fields.html.erb
<%= link_to "Remove", '#', class: "remove_fields btn btn-danger" %>
<%= f.input :product_id, collection: Product.all.somethingsomething, include_blank: false %>
<%= f.input :price %>
<%= f.input :quantity %>
Now you can choose your products from a dropdown, and set price and quantity, and whatever you need dynamically.
I have three tables via many-to-many-association: Supermarket, Product and Supply.
Each Supermarket can hold many products and each product can be sold in many supermarkets. The association is build via the Supply-model.
Supermarket:
class Supermarket < ActiveRecord::Base
attr_accessible :name, :address, :products_attributes
has_many :supplies
has_many :products, :through => :supplies
accepts_nested_attributes_for :products
end
Product:
class Product < ActiveRecord::Base
attr_accessible :name, :supermarkets_attributes
has_many :supplies
has_many :supermarkets, :through => :supplies
accepts_nested_attributes_for :supermarkets
end
Association via Supply:
class Supply < ActiveRecord::Base
attr_accessible :supermarket_id, :product_id
belongs_to :supermarket
belongs_to :product
end
I have created the scaffolds and populated the Supermarket-table.
In my Product form, i want to use one (or more) drop-down-menu(s) to select the correspondent Supermarket-name(s). Goal is to create a new product while also creating the association via the Supply-table.
What should the code look like in form and/or controller for the products if I want to select the corresponding supermarkets from there?
In you products form you need to add this line...
<%= collection_select(:product, :supermarket_ids, SuperMarket.all, :id, :name, {}, { :multiple => true } )%>
You also shouldn't need to use an accepts_nested_attributes for this, the many to many association you already have set up should take care of the rest.
I think in views
<%= f.collection_select "super_market_ids[]",#super_markets,:id,:name,{},{:multiple=>"multiple'} %>
I am not sure about super_market_ids or super_market_ids[] and syntax just verified once.
In select tag if you want checkbox type multi select there is an chosen library that will help you to build nicer UI,