I'm trying to add a set of users to a company_profile object. The idea is a user will create a company and then add more users to the company in various roles.
The company profile has an address object, and when I pull up the form on the new call I get this error:
"can't write unknown attribute company_profile_id"
company_profile -> new
<%= form_for(setup_companyProfile(#companyProfile), validate: true, html: { multipart: true }) do |f| %>
<%= f.fields_for :address do |address| %>
<%= render :partial => 'shared/address', :locals => {:f => address} %>
<% end %>
<% end %>
user.rb
belongs_to :company_profile
helper.rb
def setup_companyProfile(companyProfile)
if(companyProfile.address.present? == false)
companyProfile.address ||= Address.new
end
companyProfile
end
company_profile.rb
class CompanyProfile < ApplicationRecord
has_many :users
has_one :address
accepts_nested_attributes_for :address
end
company_profile_controller.rb
class CompanyProfileController < ApplicationController
def new
#companyProfile = CompanyProfile.new
end
def edit
#companyProfile = CompanyProfile.find(current_user.company_profile_id)
end
def update
end
def show
end
end
When you use has_one from the company-profile model... Rails expects there to be a belongs_to :company_profile on the Address model... and this belongs_to requires a column called company_profile_id on the addresses table... do you have that? If not - you will need to create a migration that adds it.
Related
I'm trying to deal with nested attributes with the usual way:
Model:
class InventoryItem < ApplicationRecord
belongs_to :location
accepts_nested_attributes_for :location
end
Form:
<div class="field">
<%= form.fields_for :location do |location| %>
<%= location.label :location_name %>
<%= location.text_field :name %>
<% end %>
</div>
Controller:
def new
#inventory_item = InventoryItem.new
#inventory_item.build_location
end
def inventory_item_params
params.require(:inventory_item).permit(:location_id, location_attributes:[:name])
end
My problem is that I want that if the Location exists the new InventoryItem is associated with it.
I don't now how to rebuild the association between InventoryItem and Location in case of Location with the name exists. The: #inventory_item.build_location in the controller is allways creating a new Location.
Thanks in advance
The accepts_nested_attributes_for :location adds a method to your InventoryItem model: location_attributes=.
You have a special constraint on this method, so you need to override it in inventory_item.rb, something like this:
#inventory_item.rb
def location_attributes=(attrs)
begin
self.location_id = Location.find_by!(name: attrs[:name])
rescue ActiveRecord::RecordNotFound
super
end
end
I have three model classes related to each other.
class Student < ActiveRecord::Base
has_many :marks
belongs_to :group
accepts_nested_attributes_for :marks,
reject_if: proc { |attributes| attributes['rate'].blank?},
allow_destroy: true
end
This class describes a student that has many marks and I want to create a Student record along with his marks.
class Mark < ActiveRecord::Base
belongs_to :student, dependent: :destroy
belongs_to :subject
end
Marks are related both to the Subject and a Student.
class Subject < ActiveRecord::Base
belongs_to :group
has_many :marks
end
When I try to create the nested fields of marks in loop labeling them with subject names and passing into in it's subject_id via a loop a problem comes up - only the last nested field of marks is saved correctly, whilst other fields are ignored. Here's my form view code:
<%= form_for([#group, #student]) do |f| %>
<%= f.text_field :student_name %>
<%=f.label 'Student`s name'%><br>
<%= f.text_field :student_surname %>
<%=f.label 'Student`s surname'%><br>
<%=f.check_box :is_payer%>
<%=f.label 'Payer'%>
<%= f.fields_for :marks, #student.marks do |ff|%>
<%#group.subjects.each do |subject| %><br>
<%=ff.label subject.subject_full_name%><br>
<%=ff.text_field :rate %>
<%=ff.hidden_field :subject_id, :value => subject.id%><br>
<%end%>
<% end %>
<%= f.submit 'Add student'%>
<% end %>
Here`s my controller code:
class StudentsController<ApplicationController
before_action :authenticate_admin!
def new
#student = Student.new
#student.marks.build
#group = Group.find(params[:group_id])
#group.student_sort
end
def create
#group = Group.find(params[:group_id])
#student = #group.students.new(student_params)
if #student.save
redirect_to new_group_student_path
flash[:notice] = 'Студента успішно додано!'
else
redirect_to new_group_student_path
flash[:alert] = 'При створенні були деякі помилки!'
end
end
private
def student_params
params.require(:student).permit(:student_name, :student_surname, :is_payer, marks_attributes: [:id, :rate, :subject_id, :_destroy])
end
end
How can I fix it?
#student.marks.build
This line will reserve an object Mark.
If you want multi marks, May be you need something like this in new action :
#group.subjects.each do |subject|
#student.marks.build(:subject=> subject)
end
Hope useful for you.
I'm using the rails3-jquery-autocomplete gem my artist field on a releases form and want to make sure any new releases use the id of an existing artist if found via autocomplete or create a new artist if not found. At the moment the autocomplete gem returns artists as expected but created a new entry even if that artist exists.
My models are as follows:
class Artist < ActiveRecord::Base
has_and_belongs_to_many :releases
end
class Release < ActiveRecord::Base
has_and_belongs_to_many :artists
accepts_nested_attributes_for :artists, :reject_if => lambda { |a| a[:name].blank? }
end
And controllers:
class ArtistsController < ApplicationController
def index
#artists = Artist.find(:all, :order => :name, :group => :name)
end
def create
#release = Release.find(params[:release_id])
#artist = #release.artists.create(params[:artist])
redirect_to release_path(#release)
end
def destroy
#release = Release.find(params[:release_id])
#artist = #release.artists.find(params[:id])
#artist.destroy
redirect_to release_path(#release)
end
end
class ReleasesController < ApplicationController
autocomplete :artist, :name
def new
#release = Release.new
#release.artists.build
end
def create
#release = Release.new(params[:release])
#release.user_id = current_user.id
end
end
My routes contains:
resources :releases do
get :autocomplete_artist_name, :on => :collection
end
And finally the artist fields:
<%= f.fields_for :artists do |builder| %>
<%= render 'artist_fields', :f => builder %>
<% end %>
(The rendered fields)
<%= f.label :name, "Artist" %><br />
<%= f.autocomplete_field :name, autocomplete_artist_name_releases_path, :class => "text" %>
I suggest you watch this railscast on autcomplete associations
It clearly does what you need.
I don't want to spoil your viewing pleasure, but it all revolves around find_or_create_by
Why don't you fetch the id of the artist with
:id_element => '#artist_id' on the f.autocomplete_field?
If the artist_id param does not come back, you can create it
I'm working on an association between two models:
class Person < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :person
end
Many person records exist in the system that don't necessarily correspond to a user, but when creating a user you need to either create a new person record or associate to an existing one.
What would be the best way to associate these two models when the person record already exists? Do I need to manually assign the user_id field, or is there a Rails way of doing that?
Where #user is a recently created user, and #person is an existing person.
#user.person = #person
#user.save
Alternately:
User.new :person => #person, ... #other attributes
or in params form:
User.new(params[:user].merge({person => #person}))
As far as forms go:
<% form_for #user do |f| %>
...
<% fields_for :person do |p| %>
<%= p.collection_select, :id, Person.all, :id, :name, :include_blank => "Use fields to create a person"%>
<%= p.label_for :name%>
<%= p.text_field :name %>
...
<% end %>
<% end %>
And in the user controller:
def create
#user = User.create(params[:user])
#person = nil
if params[:person][:id]
#person = Person.find(params[:person][:id])
else
#person = Person.create(params[:person])
end
#user.person = #person
...
end
If you don't want to create/alter a form for this, you can do
#person_instance.user = #user_instance
For has_many relationships, it would be:
#person_instance.users << #user_instance
You first have to do a nested form :
<% form_for #user do |user| %>
<%= user.text_field :name %>
<% user.fields_for user.person do |person| %>
<%= person.text_field :name %>
<% end %>
<%= submit_tag %>
<% end %>
In your User model :
class User < ActiveRecord::Base
accepts_nested_attributes_for :person
end
If you want the person deleted when the user is :
class User < ActiveRecord::Base
accepts_nested_attributes_for :person, :allow_destroy => true
end
And in your controller do nothing :
class UserController < ApplicationController
def new
#user = User.new
#find the person you need
#user.person = Person.find(:first)
end
def create
#user = User.new(params[:user])
#user.save ? redirect_to(user_path(#user)) : render(:action => :new)
end
end
Can anyone tell me why the form at the end of this question isn't working like it should?
Save doesn't work
The select-helper doesn't select the
value according to the object #kid
The whole thing is based on Rails 2.2.2 and no, upgrading to Rails 2.3 to solve this problem isn't an option. :-)
I used this recipe to build the multiple model form.
# CLASS GRANDPARENT
class Grandparent < ActiveRecord::Base
has_many :parents
end
# CLASS PARENT
class Parent < ActiveRecord::Base
belongs_to :grandparent, :class_name => "Grandparent", :foreign_key => "grandparent_id"
has_many :kids
end
# CLASS KID
class Kid < ActiveRecord::Base
belongs_to :parent, :class_name => "Parent", :foreign_key => "parent_id"
# Virtual attribute setter for new self.parent.grandparent (Grandparent) attributes
def new_grandparent_attributes=(_gp_attributes)
self.parent.build_grandparent(_gp_attributes)
end
# Virtual attribute setter for existing self.parent.grandparent (Grandparent) attributes
def existing_grandparent_attributes=(_gp_attributes)
unless self.parent.grandparent.new_record?
attributes = _gp_attributes[self.parent.grandparent.id.to_s]
if attributes
self.parent.grandparent.attributes = attributes
else
self.parent.grandparent.delete(grandparent)
end
end
end
end
# CONTROLLER KIDS
class KidsController < ApplicationController
def new
#kid = Kid.new
end
def edit
#kid = Kid.find(params[:id])
end
def create
params[:kid][:new_grandparent_attributes] ||= {}
#kid = Kid.new(params[:kid])
end
def update
params[:kid][:existing_grandparent_attributes] ||= {}
#kid = Kid.find(params[:id])
end
end
# THIS IS THE MULTI-MODEL FORM USED IN THE VIEW
<% form_for(#kid) do |f| %>
<p>
<% new_or_existing = #kid.parent.grandparent.new_record? ? 'new' : 'existing' %>
<% prefix = "kid[#{new_or_existing}_grandparent_attributes][]" %>
<% fields_for prefix, #kid.parent.grandparent do |g_f| -%>
<p>
<%= g_f.label :, 'Grandparent Name' %><br />
<!-- THE FOLLOWING FORM DOESN'T CHANGE ACCORDING TO EXISTING #child -->
<%= #grandparents = Entity.find(:all, :order => :name)
g_f.collection_select(:name ,#grandparents, :id, :name)
%>
</p>
<% end %>
</p>
<p>
<%= f.label :name, "Kid Name" %><br />
<%= f.text_field :name %>
</p>
<%= submit_tag 'Go' %>
<% end %>
Well, correct me if I am wrong but it doesn't appear that you are actually saving the object anywhere. In your create and update actions you are calling new and then not saving it.
To rectify this you could do:
def create
params[:kid][:new_grandparent_attributes] ||= {}
#kid = Kid.new(params[:kid])
if #kid.save
# successful save logic here
else
#failed save logic here
end
end
def update
params[:kid][:existing_grandparent_attributes] ||= {}
#kid = Kid.find(params[:id])
if #kid.update_attributes(params[:kid])
#successful save logic here
else
#failed save logic here
end
end
Then in your select box you are trying to find every record of Entity, not those fields of Entity that are related to #kid. In order to do this you'll have to set up a relationship between kid and grandparent.
# CLASS GRANDPARENT
class Grandparent < ActiveRecord::Base
has_many :parents
has_many :grand_kids, :through => :parents
end
# CLASS PARENT
class Parent < ActiveRecord::Base
belongs_to :grandparent, :class_name => "Grandparent", :foreign_key => "grandparent_id"
has_many :kids
end
# CLASS KID
class Kid < ActiveRecord::Base
belongs_to :parent, :class_name => "Parent", :foreign_key => "parent_id"
belongs_to :grandparent
# ...
This way you can access a kid's grandparents through by #kid.grandparents. Then you can generate the select field:
<%= g_f.collection_select(:name ,#kid.grandparents, :id, :name) %>