I have a nested object issue when creating a new item using forms. When I create a new Item within a form and view I created I get a nil on the Product object when trying to access it.
Here is my structure. I have a class called Item and a class called Product. There could be more than 1 item referring to the same Product.
class CreateItem < ActiveRecord::Migration
def change
create_table :items do |t|
t.integer :product_id
t.string :name
t.text :description
t.timestamps
end
end
end
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.text :description
t.decimal :price
t.string :image_file_name
t.integer :inventory
t.timestamps
end
end
end
class Item< ActiveRecord::Base
belongs_to :product
belongs_to :itemstatus
end
class Product < ActiveRecord::Base
has_one :shop
accepts_nested_attributes_for :item
end
Controller Code:
def create
#item= Item.new(params[:item])
if #item.save
redirect_to #item, notice: "Item successfully created!"
else
render :new
end
end
The problem is happening when I go to show the created item. The Product object is nil. I'm not sure what I should be doing to get the Product Object correctly created and added in this process of creating a new Item.
Can someone help me?
Thanks!
params[:item] must be as the following:
#first example
params[:item] = {product_id: 1}
#second example
product = product.create(name: "", description: "", ..)
params[:item] = {product: product}
#third example
#item.product = product
#or
#item.product_id = params[:item][:product_id]
Try to do that:
#item.new(name: params[:item][:name], product_id: params[:item][:product_id], description: params[:item][:description])
help link
Related
This question follows up on Rails has_many :through association: save instance into join table and I am restating things here for more clarity.
In our Rails app, there are 3 models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
And here are the corresponding migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.integer :total_calendar_count
t.integer :owned_calendar_count
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
Here is what we are trying to accomplish:
When a logged in user (current_user) creates a calendar, we should:
Create a new #calendar and save it to the Calendar table
Assign the "Creator" role to the user (current_user) for this newly created calendar through the Role column in the Administration table
Increment the total_calendar_count and the owner_calendar_count columns of the User table
In order to do that, we think we need to work on calendars#create.
In the CalendarsController, we already have the following code:
def create
#calendar = current_user.calendars.create(calendar_params)
if #calendar.save
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
And we collect data from users through the following _calendar_form.html.erb form:
<%= form_for(#calendar) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :name, placeholder: "Your new calendar name" %>
</div>
<%= f.submit "Create", class: "btn btn-primary" %>
<% end %>
We are considering updating the controller as follows:
def create
#calendar = current_user.calendars.create(calendar_params)
#current_user.total_calendar_count += 1
#current_user.owned_calendar_count += 1
current_user.administrations << #calendar.id
#calendar.administration.role = 'Creator'
if #calendar.save
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
ActiveRecord::AssociationTypeMismatch in CalendarsController#create
Administration(#70307724710480) expected, got Fixnum(#70307679752800)
unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
raise ActiveRecord::AssociationTypeMismatch, message
end
end
app/controllers/calendars_controller.rb:7:in `create'
How can we make it work?
This line is actually causing the error: current_user.administrations << #calendar.id.
current.administrations expects an object of type Administration while you are passing a Fixnum into it.
You can accomplish the same functionality in the following way:
current_user.administrations.create(calendar_id: #calendar.id)
Edit:
As OP asked in comments that it is a good practice or not. See, there is rule that says that controllers should be skinny, and models should be fatty. Well, it means you should try to write minimum code, and all the logic and fetching of objects should be there in models. But that isn't the case in your code scenario. You should move your code into model, and then call that into your controller.
Here's how:
class User < ActiveRecord::Base
def add_calendar_and_role(calendar_id, role)
self.administrations.find_by(calendar_id: calendar_id).update(role: role)
end
end
This way, your code reduces to just:
current_user.add_calendar_and_role(#calendar.id, 'Creator')
And on the same way, you can further refactor your controller code.
I am trying to make a forum application with Rails 4. I want users to have many forums and so I know I need a many-to-many relationship. I have a form to save the title and the description of the new forum. I Have 3 tables so far, users, forums, and forums_users. Everything works great when I create a new form and it gets added to the forums database. My question is how do I get the information to go to the forums_users table? Because right now when I submit my form, it does not add the information to the association table.
Here is my migration file for forums.
def up
create_table :forums do |t|
t.string :title
t.text :description
t.string :logo
t.boolean :is_active, default: true
t.timestamps
end
add_index :forums, :title
create_table :forums_users, id: false do |t|
t.belongs_to :forum, index: true
t.belongs_to :user, index: true
end
end
def down
drop_table :forums
drop_table :forums_users
end
These are my models.
class Forum < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :forums
end
Here is my create method in the Forum Controller
def create
#forum = Forum.new(forum_params)
#forum.save
respond_to do |format|
format.html{redirect_to admin_path, notice: 'New forum was successfully created.'}
end
end
private
def forum_params
params.require(:forum).permit(:title, :description, :logo, :is_active)
end
And here is the form you submit.
= simple_form_for(:forum, url: {action: :create, controller: :forums}) do |f|
= f.error_notification
.form-inputs
= f.input :title, autofocus: true
= f.input :description, as: :text
.form-actions
= f.button :submit
Thank you in advance.
If you want to get the data from your join table forum_users then use has_many :through
class Forum < ActiveRecord::Base
has_many :users, through: :forum_users
end
class User < ActiveRecord::Base
has_many :forums, through: :forum_user
end
class ForumUser < ActiveRecord::Base
belongs_to :user
belongs_to :forum
end
Now you can access/fetch the forum_users table data using UserForum Model
Create the forum using a reference to the current user, for example:
#forum = current_user.forums.create(forum_params)
I have a form for a model called isp, which 'has_many' isp accounts. the isp account belongs to to 'isp'.
There is a validation on the isp_account that means it cant be added if there isnt an isp_id, so my reasoning is to created a nested form. I created the nested form like so
= simple_form_for #isp, :html => { :multipart => true } do |f|
= f.input :title
= f.simple_fields_for :isp_accounts do |tag|
= tag.input :title, label: "Tag Name"
however the nested attribute isnt being displayed. There are no errors etc. Why is this? Am I approaching this in the best way? is this the only way?
here's the code
ISP MODEL
class Isp < ActiveRecord::Base
has_many :isp_accounts, dependent: :destroy
has_many :deployments, through: :servers
has_many :servers, through: :isp_accounts
validates :title, presence: true
accepts_nested_attributes_for :isp_accounts
end
ISP ACCOUNTS MODEL
class IspAccount < ActiveRecord::Base
belongs_to :isp
has_many :deployments, through: :servers
has_many :servers, dependent: :destroy
validates :title, presence: true
validate :check_associates
private
def check_associates
associated_object_exists Isp, :isp_id
end
end
ISP ACCOUNT CONTROLLER
....
def new
#isp_account = IspAccount.new
end
def update
#isp_account.update_attributes(isp_accounts_path)
if #isp_account.save
record_saved
return redirect_to(isp_accounts_path)
else
check_for_errors
return render('/isp_accounts/edit')
end
end
private
def get_isp_accounts
#isp_account = IspAccount.all
end
def get_isp_account
#isp_account = IspAccount.find(params_isp_accounts)
end
def params_isp_accounts
params.require(:isp_account).permit!
end
end
....
def new
#isp = Isp.new
end
def update
#isp.update_attributes(params_isp)
if #isp.save
record_saved
return redirect_to(isps_path)
else
check_for_errors
return render('new')
end
end
private
def params_isp
params.require(:isp).permit(:title, isp_accounts_attributes: [:id, :title])
end
def get_isp
#isp = Isp.where(id: params[:id]).first
unless #isp
record_not_found
return redirect_to(isps_path)
end
end
def get_isps
#isp = Isp.all.order(:title)
end
end
SCHEMA
create_table "isp_accounts", force: true do |t|
t.string "title"
t.integer "isp_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "isps", force: true do |t|
t.string "title"
t.datetime "created_at"
t.datetime "updated_at"
end
ok i got it. I was missing the new bit for that attribute in my controller. pretty basic really.
def new
#isp = Isp.new
#isp.isp_accounts.new
end
I have the following 2 tables defined in migrations
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :name
t.string :phone
t.string :email
t.string :address
t.string :resume
t.timestamps
end
end
end
Class CreateResumeSections < ActiveRecordMigration
def self.up
create_table :resume_sections do |t|
t.string :section_name
t.string :html
t.timestamps
end
end
end
I have following 2 models
class User
has_many :resume_sections, :dependent => :destroy
attr_accessor :section_layout
after_save :save_sections
private
def save_sections
self.section_layout = ###Someother logic here that sets this variable
end
end
class ResumeSection
belongs_to :user
end
In my users_controller, I have the following code
class UserController < ApplicationController
def create
#user = User.new(params[:user])
#user.save
#user.section_layout.each {|key,value|
rs = ResumeSection.new(:section_name => key, :html => value, :user => #user)
rs.save
}
end
end
In my view I have the following code
<% #user.resume_sections.each do |section| %>
<%= section.section_name %>
<%= section.html %>
<% end %>
I get Undefined method error for Nil:NilClass in the view. The expression #user.resume_sections is not returning to me the records that I just created and saved in the UsersController. Instead it returns nil to me. When I check the database the records are there.
Is the expression #user.resume_sections the correct expression to access these records?
Thanks
Paul
It seems to me that your you missed something in you migrations. ResumeSection needs to have and integer field called user_id. Just create a new migration that has something like this in it:
def change
add_column :resume_section, :user_id, :integer
end
I am attempting to associate 2 models - Athletes and Results
They have the following fields:
Athletes - :name :year :gender
Results - :name :event :performance
I have added belongs_to :athlete to results.rb & added has_many :results to athletes.rb
I would like to use the :name attribute to act as the primary key used to associate the two models as the intention is for all athletes to be uploaded initially and then just use the results input for the remainder of the season.
I have edited the results_controller to the following:
def create
#this was the initial code....
##result = Result.new(params[:result])
# This is the new code to try set up an association
#athlete = Athlete.where('name = ?', 'Peter')
#result = #athlete.results.create(params[:result])
respond_to do |format|
if #result.save
format.html { redirect_to #result, notice: 'Result was successfully created.' }
format.json { render json: #result, status: :created, location: #result }
else
format.html { render action: "new" }
format.json { render json: #result.errors, status: :unprocessable_entity }
end
end
end
This however produces the error undefined method 'results' for #<ActiveRecord::Relation:0x36a2b28>. I was also hoping to use the line #athlete = Athlete.where("name = ?", params[:name]) however it keeps yielding a NULL parameter value...
Is anyone able to point me in the correct direction please?
Extra information:
Results migration
class CreateResults < ActiveRecord::Migration def change
create_table :results do |t|
t.string :name
t.string :event
t.decimal :performance
t.timestamps
end
#add in new line here
add_index :results, :name
end end
Athletes migration
class CreateAthletes < ActiveRecord::Migration
def change
create_table :athletes do |t|
t.string :name
t.integer :year
t.string :gender
t.timestamps
end
end
end
Result.rb:
class Result < ActiveRecord::Base
belongs_to :athlete
end
Athlete.rb
class Athlete < ActiveRecord::Base
has_many :results
end
The problem is that Athlete.where('name = ?', 'Peter') returns ActiveRecord::Relation object. Either use
Athlete.where('name = ?', 'Peter').first
or the dynamic find method
Athlete.find_by_name('Peter')
Edit
Also make sure you add t.references :athlete to your results table.
Your results table needs to store the athlete_id.
create_table :results do |t|
t.references :athlete
t.string :name
t.string :event
t.decimal :performance
t.timestamps
end
The references will create the foreign key association using the rails conventions (which in this case is athlete_id)