Placing data from associated table in view - ruby-on-rails

I have a class called Imprintable that contains this
class Imprintable < ActiveRecord::Base
has_one :brand
# ...
I also have a class called Style that contains the following:
class Style < ActiveRecord::Base
belongs_to :imprintable
# ...
My schema for Styles contains a foreign key to the imprintable table in the form of an integer called imprintable_id
I'm trying to display an attribute from the Style table called catalog_no in a view to edit information about an imprintable. I know what I have is wrong because style doesn't exist as a member of the imprintable table, but I'm not sure how to access the name of the catalog_no from the corresponding entry in the styles table. HTML is like this:
<!-- language: HTML -->
<div class="box-info">
<%= render partial: 'shared/modal_errors', locals: {object: imprintable} %>
<%= form_for(imprintable) do |f| %>
<div id="horizontal-form" class="collapse in">
<!-- Lots of HTML.. -->
<div class="form-group">
<%= f.label :style.catalog_no, class: 'col-sm-2 control-label' %>
<div class="col-sm-10">
<%= f.text_field :style.catalog_no, class: 'form-control' %>
<!-- Problem is on the above line!! -->
<p class="help-block">The catalog number of the imprintable
</div>
</div>
</div>
<% end %>
</div>
Thanks for your time!

You should use nested form
Model Imprintable looks like this :
class Imprintable < ActiveRecord::Base
has_one :brand
accepts_nested_attributes_for :style
end
In controller edit action
def edit
#imprintable = Imprintable.find(params[:id])
end
In the view file edit.html.erb looks like
<div class="box-info">
<%= render partial: 'shared/modal_errors', locals: {object: imprintable} %>
<%= form_for(#imprintable) do |f| %>
<div id="horizontal-form" class="collapse in">
<%= f.fields_for :style do |d| %>
<div class="form-group">
<%= d.label :catalog_no, class: 'col-sm-2 control-label' %>
<div class="col-sm-10">
<%= d.text_field :catalog_no, class: 'form-control' %>
<p class="help-block">The catalog number of the imprintable
</div>
</div>
<% end %>
</div>
<% end %>
</div>
If you're using rails 4, don't forget add style_attibutes to impritable_params method
private
def impritable_params
## params.require(:your_model).permit(:fields_of_model, association_model_attributes: [:fields_of_association_models])
params.require(:imprintable).permit(style_attributes: [:id, :catalog_no])
end
end

Related

Rails 5.2: How to create fields_for each I18n key value?

In my _form.html.erb I have nested fields, where for an Offer I would like to save multiple Discount types with values:
<%= f.fields_for #offer.discounts do |discount| %>
<% I18n.t(:discount_type).each do |type| %>
<div class="form-group row discount-list">
<label class="col-sm-8 control-label">
<%= discount.label I18n.t("discount_type.#{type[0]}") %><br/>
</label>
<div class="col-sm-4">
<%= discount.hidden_field :discount_type, value: type[0] %>
<%= discount.number_field :value,
value: (#offer.new_record? ? '0.00' : discount.value),
class: "form-control allow_numeric" %>
</div>
</div>
<% end %>
<% end %>
At the moment my form is populated correctly as I would like it to be, however values are not saving since:
in my params I see only 1 of 3 discount types like this:
"seller_discount"=>{"discount_type"=>"special", "value"=>"5"}
there is error Unpermitted parameter: :seller_discount
records are not saving
My Seller::Offer model looks like this:
has_many :offer_discounts, class_name: "Seller::OfferDiscount"
has_many :discounts, class_name: "Seller::Discount", through: :offer_discounts, inverse_of: :offers
accepts_nested_attributes_for :discounts, allow_destroy: true
My controller is simple as:
def new
#offer = Seller::Offer.new
end
private
def offer_params
params.require(:seller_offer).permit(
:company_id, :name, :base_price,
discounts_attributes: [:id, :discount_type, :value, :_destroy]
)
end
So far I've been trying different ideas from Rails docs, however no luck. Probably in my specific case, twist is where I try to iterate over I18n.t(:discount_type) an create input field for each discount type (buy key).
I'll be happy for any hint how to solve this. Thank you!
Since you're iterating over discount_type I think that needs to be an array type in your offer_params method.
def offer_params
params.require(:seller_offer).permit(
:company_id, :name, :base_price,
seller_discounts_attributes: [:id, :discount_types => [:discount_type, :value], :_destroy]
)
end
But what happens if you try to use fields_for helper?
<%= f.fields_for #offer.discounts do |discount| %>
<%= f.fields_for I18n.t(:discount_type) do |type| %>
<div class="form-group row discount-list">
<label class="col-sm-8 control-label">
<%= discount.label I18n.t("discount_type.#{type[0]}") %><br/>
</label>
<div class="col-sm-4">
<%= discount.hidden_field :discount_type, value: type[0] %>
<%= discount.number_field :value,
value: (#offer.new_record? ? '0.00' : discount.value),
class: "form-control allow_numeric" %>
</div>
</div>
<% end %>
<% end %>
So, to have my form working for both New and Edit actions, final solution is this:
<% if params[:action] == 'new' %>
<div class="col-md-7 col-sm-7">
<!-- Discounts for new form !-->
<% I18n.t(:discount_type).each do |type| %>
<%= f.fields_for :discounts, #offer.discounts.build do |disc| %>
<div class="form-group row discount-list">
<label class="col-sm-8 control-label">
<%= disc.label I18n.t("discount_type.#{type[0]}") %><br/>
</label>
<div class="col-sm-4">
<%= disc.hidden_field :discount_type, value: type[0] %>
<%= disc.number_field :value, value: '0.00',
class: "form-control allow_numeric" %>
</div>
</div>
<% end %>
<% end %>
</div>
<% elsif params[:action] == 'edit' %>
<div class="col-md-7 col-sm-7">
<!-- Discounts for edit form !-->
<%= f.simple_fields_for :discounts do |d| %>
<div class="form-group row discount-list">
<%= d.input :discount_type, as: :hidden %>
<label class="col-sm-8 control-label">
<%= d.label I18n.t("discount_type.#{d.object.discount_type}") %><br/>
</label>
<div class="col-sm-4">
<%= d.input :value, label: false, input_html: { id: d.object.discount_type+"_discount",
class: "form-control allow_numeric" } %>
</div>
</div>
<% end %>
</div>
<% end %>
Edit action is done with simple_form_fields_for
Obviously not shiny solution, but looks like this works.

Rails how to save Address from different models (Country, State, City, Address) in one single form?

So what I'm trying to do is save in one single form a bunch of data. Part of that data is an address. This address has been split in several models: Country, State, City and Address.
My problem is that honestly I have no idea how to manage that in the controller. I will share what I have at the moment and hope someone can give me a clue.
Should I do accepts_nested_attributes_for for all models?. How can I connect all this inherits fields or foreign key in order to process the information?
Country Model:
class Country < ApplicationRecord
has_many :states
end
State Model:
class State < ApplicationRecord
belongs_to :country
has_many :cities
end
City Model:
class City < ApplicationRecord
belongs_to :state
has_many :addresses
accepts_nested_attributes_for :addresses
end
Address Model:
class Address < ApplicationRecord
belongs_to :city
belongs_to :office
end
Office Model:
class Office < ApplicationRecord
has_many :addresses, inverse_of: :office
accepts_nested_attributes_for :addresses
end
Office Controller Strong Params for Country, State, City and Address (There are more params, I just placed those regarding to the issue I'm having):
def office_params
params.require(:office).permit(
{
countries_attributes: [
:id, :name,
states_attributes: [
:id, :name,
cities_attributes: [
:id, :name,
addresses_attributes: [
:id, :street, :state_id
]
]
]
]
},
)
Office New Method:
def new
#office = Office.new
#countries = Country.all.map{|c| [ c.name, c.id ] }
#states = State.all.map{|c| [ c.name, c.id ] }
##office.addresses.build.cities.build
##office.addresses.counties.states.cities.build
##office.addresses.build
end
Office Form: In this form I'm using fields_for in order to nested all the fields called from their respective models. So here I need to save all information at once to several models.
<%= form_with(model: office, local: true, html: {class: "form-office"}) do |form| %>
<% if office.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(office.errors.count, "error") %> prohibited this office from being saved:</h2>
<ul>
<% office.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="row">
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="form-group">
<span><%= form.label :office_name %></span>
<%= form.text_field :office_name, class: 'form-control' %>
</div>
</div>
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="form-group">
<span><%= form.label :office_slug %></span>
<%= form.text_field :office_slug, class: 'form-control' %>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="form-group">
<span><%= form.label :office_email %></span>
<%= form.text_field :office_email, class: 'form-control' %>
</div>
</div>
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="form-group">
<span><%= form.label :phone %></span>
<%= form.text_field :phone, class: 'form-control' %>
</div>
</div>
</div>
<hr>
<h3 style="color: #B0B0B0;">Office Address</h3>
**Office Address**
<div class="row">
<div class="col-md-6 col-sm-6 col-xs-12 col-md-offset-6">
<%= form.fields_for :countries do |country| %>
<div class="form-group">
<span><%= country.label :country %></span>
<%= select_tag(:country_id, options_for_select(#countries), class: 'form-control', :prompt => "Select Country") %>
</div>
<div class="form-group">
<%= country.fields_for :states do |state| %>
<span><%= state.label :state %></span>
<%= select_tag(:state_id, options_for_select(#states), class: 'form-control', :prompt => "Select State") %>
</div>
</div>
<div class="col-md-6 col-sm-6 col-xs-12 col-md-offset-6">
<div class="form-group">
<%= state.fields_for :cities do |city| %>
<span><%= city.label :city %></span>
<%= city.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= city.fields_for :addresses do |address| %>
<span><%= address.label :street %></span>
<%= address.text_field :street, class: 'form-control' %>
</div>
<div class="form-group">
<span><%= address.label :zip_code %></span>
<%= address.text_field :zip_code, class: 'form-control' %>
</div>
<% end %>
<% end %>
<% end %>
<% end %>
</div>
</div>
<hr>
<h4 style="color: #B0B0B0;">Comment</h4>
<div class="row">
<div class="col-md-12 col-sm-6 col-xs-12 st">
<div class="comments">
<%= form.fields_for :comments do |comment_form| %>
<%= render 'comment_fields', f: comment_form %>
<% end %>
</div>
</div>
</div>
<div>
<div class="col-md-4 offset-md-4">
<%= form.submit class: 'btn btn-lg' %>
</div>
</div>
<% end %>
Yes, you have to add accepts_nested_attributes_for for every association you want to update through fields_for.
class Country < ApplicationRecord
has_many :states
accepts_nested_attributes_for :states
end
class State < ApplicationRecord
belongs_to :country
has_many :cities
accepts_nested_attributes_for :states
end
However, if you do that and if you define office_params correctly, saving all nested models is possible with single Office.create(office_params) call, without worrying about setting foreign keys.
Same goes for updating, but you will have to provide hidden :id form fields for nested models, so existing model instances can be found in DB.
You should also consider adding validates_associated to models.

accepts_nested_attributes_fields not showing for join table

I have the following tables
class Region < ActiveRecord::Base
has_many :companies, through: :companies_regions
has_many :companies_regions, :dependent => :destroy
end
class Company < ActiveRecord::Base
has_many :regions, through: :companies_regions
has_many :product_type, dependent: :destroy
has_many :companies_regions, :dependent => :destroy
accepts_nested_attributes_for :companies_regions, :allow_destroy => true
end
class CompaniesRegion < ActiveRecord::Base
belongs_to :company
belongs_to :region
end
I want to create a new company and i want to be able to add a new region accordingly to the CompaniesRegion table.
form.html.erb
<%= simple_form_for(['admin', #company]) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<%= f.input :name %>
</div>
<div>
<div class="row">
<div class="col-md-12">
<h4>Basic Coverages</h4>
<div class="row form-group">
<label class="col-md-1">#</label>
<label class="col-md-3">Coverage</label>
<label class="col-md-1">Description</label>
</div>
<div>
<%= f.simple_fields_for :companies_regions do |company_region| %>
<%= render 'company_region', f: company_region %>
<% end %>
<%= link_to_add_association 'New Region', f, :companies_regions, partial: 'company_region' %>
</div>
</div>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
_company_region.html.erb
<div class="nested-fields form-group row">
<div class="col-md-1"></div>
<div class="col-md-3">
<%#= f.select :region_id, class: 'form-control', placeholder: 'Region' %>
<%= f.input_field :region_id, collection: ['Asia', 'America'], class: 'form-control', prompt: 'Please Select' %>
</div>
<div class="col-md-1">
<%= link_to_remove_association(f, title: 'Remove') do %>
<span class="glyphicon glyphicon-remove"></span>
<% end %>
</div>
</div>
The problem here is that when i click the New Region link, i am expecting it to display the details in the _company_region.html.erb but unfortunately it doesnt.
Nothing shows. It doesnt display any data. It however refreshes the page which is absurd.
Dont know if it is because my table is a join table hence the problem or if there is something else that i am missing but based on documentation, this should be fine and should work.
Any help is deeply appreciated
Its a very common misconception that you need to use nested attributes to set assocations. You don't. Rails generates a regions_ids= setter for the association that can be hooked up with checkboxes or a select tag.
All you really need with SimpleForm is:
<%= f.association :regions %>
See the docs for ActionView::Helpers::FormOptionsHelper and associations in SimpleForm for more details on how this works.
<%= simple_form_for(['admin', #company]) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<%= f.input :name %>
</div>
<div>
<div class="row">
<div class="col-md-12">
<h4>Basic Coverages</h4>
<div class="row form-group">
<label class="col-md-1">#</label>
<label class="col-md-3">Coverage</label>
<label class="col-md-1">Description</label>
</div>
<div>
<%= f.association :regions %>
</div>
</div>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
The only reason you would need to use nested attributes is if the user must be able to create regions in the same request. But usually its better to use ajax to handle those cases.
Your code looks fine. The behaviour you describe seems to indicate that the cocoon.js code is not included/loaded correctly? Did you
add require 'cocoon' in application.js ?
did you include application.js in your html?

Rails form populated from one table and saving to another

Title kinda says it all, Basically i have this form
<% provide(:title, "Edit user") %>
<h1>Editing event:
<%= #newevent.id %></h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= simple_form_for #newevent do |f| %>
<div class="form-group">
<%= f.label :eventname %>
<div class="row">
<div class="col-md-6">
<%= f.text_field :eventname, :autofocus => true, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<%= f.input :event_type, :collection => ['Concert','Festival','Sports','Theatre'] %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label :eventdesc %>
<div class="row">
<div class="col-md-6">
<%= f.text_field :eventdesc, :autofocus => true, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label :eventshortdesc %>
<div class="row">
<div class="col-md-6">
<%= f.text_field :eventshortdesc, :autofocus => true, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label :pagetitle %>
<div class="row">
<div class="col-md-6">
<%= f.text_field :pagetitle, :autofocus => true, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label :metatag %>
<div class="row">
<div class="col-md-6">
<%= f.text_field :metatag, :autofocus => true, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label :eventvenuename %>
<div class="row">
<div class="col-md-6">
<%= f.text_field :eventvenuename, :autofocus => true, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<%= f.input :time, type: "time", :autofocus => true, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<%= f.input :date, type: "date", :autofocus => true, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label :eventimage %>
<div class="row">
<div class="col-md-6">
<%= f.text_field :eventimage, :autofocus => true, class: "form-control" %>
</div>
</div>
</div>
<%= f.submit "Save changes", class: "btn btn-info" %>
<%= link_to "Delete", event_path(#newevent), :method => :delete, class: "btn btn-danger" %>
<% end %>
</div>
</div>
Here is what it loads from.
def edit
#newevent = Event.find(params[:id])
end
However its different now. I am wanting the stuff to load form the Master table (so the fields are populated from Master) However i have an update method that looks like this
def update
#newevent = Event.find(params[:id])
if #newevent.update_attributes(event_params.merge createdby: current_user.id)
flash[:success] = "Profile updated"
redirect_to "/events"
else
flash.now[:alert] = 'Error updating event'
end
end
Do i need to change something in the eventparams to get this to work (and make change from Event to Master) The fields are different in both tables, So would i need to make the value of the fields be something like this?
value: "<%=Master.name%>"
Thanks
Sam
If you're trying to load "defaults", you'd be better doing it in the model layer:
#app/models/event.rb
class Event < ActiveRecord::Base
before_create :set_defaults
private
def set_defaults
default = Master.first
self.attributes.except("id", "created_at", "updated_at").each do |field|
self[field] ||= default.send(field)
end
end
end
The above will pull a record from the Master model, populate any of the attributes in your Event model which have not been populated.
However, I believe your pattern to be inefficient.
The idea you are pulling "default" data from another model/table directly contradicts the DRY and modular principles of software development.
Not to say that if you wanted to create "dynamic" defaults for a model, your pattern might work. If you're trying to store exactly the same data in different models, you've got a problem.
I would do the following:
#app/models/event.rb
class Event < ActiveRecord::Base
##defaults: {
x: "y",
y: "z",
z: "0"
}
before_create :set_defaults
private
def set_defaults
self.attributes.except("id", "created_at", "updated_at").each do |field|
self[field] ||= ##defaults[field]
end
end
end
Update
After a discussion, it became apparent that there was more context required to understand the issue/solution fully.
The app works by importing a series of CSV data into the Master table. Whilst I don't know what data this is, the OP said that each Event would be built around the data in Master.
He mentioned he needed 3 fields definitely from Master whilst the others could be inputted by the user into Event.
This means that you could tie the two together with a has_many / belongs_to relationship:
#app/models/master.rb
class Master < ActiveRecord::Base
has_many :events
end
#app/models/event.rb
class Event < ActiveRecord::Base
belongs_to :master
end
This is a standard ActiveRecord association, which means you'll be able to call #master.events & #event.master -- accessing values from each table:
#app/controllers/events_controller.rb
class EventsController < ApplicationController
def new
#event = Event.new
end
def create
#event = Event.new event_params
#event.save
end
private
def event_params
params.require(:event).permit(:master_id, ...)
end
end
#app/views/events/new.html.erb
<%= form_for #event do |f| %>
<%= f.collection_select :master_id, Master.all, :id, :name %>
<%= f.submit %>
<% end %>
--
The reason why this will be much better than your current pattern is that each time you create a new event, you'll be able to access the master attributes:
#event.master.url #-> "url" from master record
In Rails in general you create bound form inputs by using a model and passing it to form_for (or simple_form_for).
In this case if you really need to have dynamic user editable default the most obvious solution would be to use Master to seed the new Event.
In most cases however defaults should be a developer concern which are handled on the model layer as suggested by Rich Peck.
Also setting defaults only really makes sense for a new record - why would you want to override the users choices when updating?
def new
#master = Master.last
#event = Event.new(#master.attributes.except("id", "created_at", "updated_at"))
end
<%= simple_form_for #newevent do |f| %>
<%= f.input :eventname %>
<% end %>

Rails - Show associated data on Index view

I'm struggling to have a belongs_to model iterate correctly inside a partial in an index page.
Classes:
class Chapter < ActiveRecord::Base
attr_accessible :name, :chapter_num,
belongs_to :chapter
#fields: :id, :name, :chapter_num
end
class County < ActiveRecord::Base
attr_accessible :name, :county_num, :chapter_id
has_many :counties
#fields: :id, :name, :county_num, :chapter_id
end
class ChaptersController < ApplicationController
def index
#chapters = Chapter.all
#counties = County.all(:joins => :chapter, :select => "counties.*, chapters.id")
end
end
app/views/chapters/index.html.erb:
<h1>Chapter Index</h1>
<%= render #chapters %>
<br />
<%= link_to "Add a new Chapter", new_chapter_path, class: "btn btn-large btn-primary" %>
app/views/chapters/_chapter.html.erb:
<div class="row">
<div class="span5 offset1"><h4><%= link_to chapter.name, edit_chapter_path(chapter.id) %></h4></div>
<div class="span2"><h4><%= chapter.chapter_num %></h4></div>
</div>
<!-- here's where the problem starts -->
<% #counties.each do |county| %>
<div class="row">
<div class="span4 offset1"><%= county.name %></div>
<div class="span4 offset1"><%= county.county_num %></div>
<div class="span2"><%= link_to 'edit', '#' %></div>
</div>
<% end %>
<%= link_to "New county", new_county_path %>
<hr>
The current code shows the screenshot below. The problem is it's iterating through all the counties, not just the counties associated with a given chapter.
How do I add a chapter specific variable within the partial that will cause the counties to iterate based upon the :chapter_id field since I'm in the index view, not the show view?
class ChaptersController < ApplicationController
def index
#chapters = Chapter.all
# #counties = County.all(:joins => :chapter, :select => "counties.*, chapters.id")
end
end
View:
<% chapter.counties.each do |county| %>
I think something like this would work for you:
<%= #chapters.each do |chapter| %>
<div class="row">
<div class="span5 offset1"><h4><%= link_to chapter.name, edit_chapter_path(chapter.id) %></h4></div>
<div class="span2"><h4><%= chapter.chapter_num %></h4></div>
</div
<% chapter.counties.each do |county| %>
<div class="row">
<div class="span4 offset1"><%= county.name %></div>
<div class="span4 offset1"><%= county.county_num %></div>
<div class="span2"><%= link_to 'edit', '#' %></div>
</div>
<% end %>
<%= link_to "New county", new_chapter_county_path(chapter) %>
<% end %>
Note that the key is to understand that because each chapter has many counties, you should iterate through the counties of each chapter via chapter.counties.each which will give you only the counties that belong to that particular chapter.
Also note the different link_to path for creating a new county. If you have your routes set up such that counties are nested under chapters, you should be able to do new_chapter_county_path(chapter)

Resources