Backwards nested form? - ruby-on-rails

Normally people make nested forms where nested attributes are accepted for objects that 'belong to' the main object...
For example:
class Brand < ActiveRecord::Base
has_many :models
end
class Model < ActiveRecord::Base
belongs_to :brand
end
So, given the above, one might expect to make a nested form for brand which accepts nested attributes for model.
This may sound ridiculous, but what I would like to do is create a form for model which accepts nested attributes for brand... is this possible?

Yes, you can.
class Brand < ActiveRecord::Base
has_many :models
end
class Model < ActiveRecord::Base
belongs_to :brand
accepts_nested_attributes_for :brands
end
After in view make
= form_for Model.new do |f|
%p
= f.label :name
= f.text_field :name
%p
Brands:
- 4.times do
= f.fields_for :brand, Brand.new do |bf|
= br.text_field :name
...
This form will generate params for brands like this:
model: {
name: "Shiny Ann",
brands_attributes: {
"0": {name: "Brand1"},
"1": {name: "Brand2"},
"2": {name: "Brand3"}
}
}

Related

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

Creating a belongs to association on a two way Many to Many

QUESTION:
How would I create an association be between the a new model: post.rb and existing 2 models: category + work models? And how would I pluck the post.rb's attribute content:text, so that I may add a unique post from the work#new -> _form partial
I have a many to many relationship between work and category models :through categorywork
I would like to add a new model called post with an attribute called content:text I would like the association of this model to be, as such: Every Work has many categories, each category inside work has a unique post.
Unfortunately, I don't know how to properly draw out or explain this association, so I will draw a makeshift schema below for reference along with pictures.
e.g: textual reference
One Work
name: project A
Many Categories
project A -> category name:UxD
project A -> category name:ID
project A -> category name:Development
Unique Post
project A -> UxD -> post content:lorem ipsum 1
project A -> ID -> post content:lorem ipsum 2
project A -> Development -> post content:lorem ipsum 3
e.g: graphical reference
work#index
work#show?category1
work#show?category2
Models
How would I write the association to connect post.rb to the preexisting many-to-many relationship
category.rb
class Category < ActiveRecord::Base
validates :name, :presence => true
has_many :categoryworks
has_many :works, :through => :categoryworks
accepts_nested_attributes_for :works
attr_accessible :description, :name, :category_ids, :svg
end
work.rb
class Categorywork < ActiveRecord::Base
validates :name, :presence => true
has_many :categoryworks
has_many :categories, :through => :categoryworks
accepts_nested_attributes_for :categories
attr_accessible :name, :subtitle, :category_ids, :svg
end
categorywork.rb
class Categorywork < ActiveRecord::Base
belongs_to :category
belongs_to :work
attr_accessible :category_id, :work_id
end
Controllers
works_controller: example of many to many association in the new controller
What would I add to work_controller.rb to gain access of the post.rb attr content:text ?
def new
#work = Work.new
#all_categories = Category.all
#work_category = #work.categoryworks.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #work }
end
end
Views
_form Partial: showing the many-to-many between categories and Works
What would I write to add the post.rb attr content:text to each check box this form
<div class="field">
<%= hidden_field_tag "work[category_ids][]", nil %>
<% Category.all.each do |category| %>
<%= check_box_tag "work[category_ids][]", category.id, #work.category_ids.include?(category.id), id: dom_id(category) %>
<%= label_tag dom_id(category), category.name %><br>
<% end %>
</div>

Can't mass-assign protected attributes: students

class Student < ActiveRecord::Base
attr_accessible :dob, :grade_status, :school_id
belongs_to :school
end
class School < ActiveRecord::Base
attr_accessible :location, :name
has_many :students
end
class HomeController < ApplicationController
def index
#school = School.new
#student = #school.students.build(params[:student])
School.create(params[:school])
end
end
add accepts_nested_attributes_for :students in the School model and add :students_attributes to the attr_accesible
Add :students to your list of attr_accessible. And buy on book about Rails.
It looks like your params hash is including the 'students' key inside of the 'school' key. Is this accurate? It looks something like this:
{ school: { name: 'Foo', location: 'Bar', students: [...] } }
I will assume this is the case. You should use nested attributes, add:
accepts_nested_attributes_for :students
to your School model. Also add students_attributes to your attr_accessible line in your School model.
In your view, you will need to use the fields_for helper so that Rails can build the students_attributes key in your params. It would look something like this:
form_for #school do |f|
f.text_field :name
f.text_field :location
f.fields_for :students do |builder|
builder.text_field :dob
...
(this should all be in ERB, Haml, or whatever you're using)
Here is the Railscast on nested forms: http://railscasts.com/episodes/196-nested-model-form-part-1 if you are still having trouble.

Rails - nested model: Can't mass-assign protected attributes

I have two models, Car and Manufacturer. These models are pretty simple:
class Car < ActiveRecord::Base
attr_accessible :manufacturer_id, :car_name, :descr, ...
belongs_to :manufacturer
...
end
and
class Manufacturer < ActiveRecord::Base
attr_accessible :name, :url
has_many :cars
...
end
The view (views/cars/_form.html.haml) with form for entering data:
= form_for #car do |f|
.field
= f.label :car_name
= f.text_field :car_name
...
= f.fields_for #manufacturer do |m|
.field
= m.label :name
= m.text_field :name
...
When I send the form for saving entered information (it goes to CarsController), I get this error:
Can't mass-assign protected attributes: manufacturer
I've tried to add the
accepts_nested_attributes_for :manufacturer
to the Car model, but it didn't help me...
Where is the problem?
EDIT:
How I am saving data in controller:
#manufacturer = Manufacturer.new(params[:car][:manufacturer])
#car = #manufacturer.cars.build(params[:car])
EDIT2:
Data from log:
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"4vcF5NV8D91DkxpCsqCzfbf05sOYsm7ssxZvPa3+kXo=",
"car"=>{"car_name"=>"...",
"descr"=>"...",
"categroy_ids"=>["2",
"3",
"4"],
"manufacturer"=>{"name"=>"Company",
"url"=>"..."}},
"commit"=>"Save",
"id"=>"..."}
Thank you
Can you save manufacturer through car?
Add to Car model:
accepts_nested_attributes_for :manufacturer
Add manufacturer_attributes among other Car attributes to attr_accessible call in Car model:
attr_accessible :manufacturer_attributes, :car_name, :descr, ...
Save it in your controller action(standard way) something like this:
def create
#car = Car.new(params[:car])
if #car.save
redirect_to #car
else
render :new
end
end
Make sure that everything you are sending in manufacturer_attributes hash is white listed with attr_accessible call in Manufacturer model(:name, :url etc..).
Your params[:car] contains manufacturer attributes.. Try this:
#manufacturer = Manufacturer.new(params[:car].delete(:manufacturer))
#car = #manufacturer.cars.build(params[:car])
You are not making use of has_many relation by doing this way. You can go through this
You need to add
attr_accessible :manufacturer_id, :car_name, :descr, :manufacturer_attributtes
in car the model. Don't bother with #manufacturer in your saving method in the car controller it is taken care of.
You should read this : Active Record Nested Attributes
I hope it helped.

Rails and has_many through association - how to properly set the form?

I am not sure I am setting my models correctly, so I would like to show you my idea:
I have the model Car that belongs to Company. Then I have a model called Color. In this DB table are stored all colors (red, blue, ...). And then there is the 4th model, called CarColor. This model contains two columns - job_id and color_id.
In the view, I would like to allow visitors to pick out colours with using checkboxes.
Form partial
= form_for #car do |f|
.field
= f.label :name
= f.text_field :name
.field
= f.label :location
= f.text_field :location
.field
= fields_for #car_colors do |cc|
...
.field
= fields_for #company do |c|
.field
= c.label :name
= c.text_field :name
.actions
= f.submit 'Save'
Models
class Company < ActiveRecord::Base
has_many :cars
end
class Car < ActiveRecord::Base
belongs_to :company
has_many :car_colors
has_many :c_colors, :through => :car_colors
end
class Color < ActiveRecord::Base
has_many :car_colors
has_many :cars, :through => :car_colors
end
class CarColor < ActiveRecord::Base
belongs_to :car
belongs_to :color
end
Saving Cars + Company works well, but I don't know how to add the checkboxes with colours in the view.
EDIT:
Regarding to the thread in the comment, I made a progress. However, I found an error that I don't know how to solve.
I am using model structure shown above and this is how look like the view:
- Color.order('name').each do |clr|
= check_box_tag :c_color_ids, clr.id, #car.car_colors.include?(clr), :name => 'car[c_color_ids][]'
= label_tag :c_color_ids, clr.name
This is the error I got:
PG::Error: ERROR: relation "car_colors" does not exist
What am I missing? How the relation cannot exist?
Ok, I spent a day of solving this issue. The scheme about is correct, the problem in my case was, that I have create a migration with table name car_color instead of car_colors...

Resources