Issues calling a method with multiple arguments - ruby-on-rails

I am new to Ruby and Rails so sorry if this looks too noob.
I have created a resource called stream and another resource called tasks and have mapped them properly using has_many and belong_to. Everything works until I decided to add a "Quick Task Add form" on my Stream.show view:
Here is the view code for the form:
<%= form_for(#task) do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class="field">
<%= f.text_field :title %> <%= f.submit "Add Task" %>
<%= hidden_field_tag(:stream_id, #stream.id) %>
</div>
<% end %>
Here is my Stream.show action:
def show
#stream = Stream.find(params[:id])
#user = User.find(#stream.user_id)
#tasks = #stream.tasks.paginate(:page => params[:page])
#title = #stream.title
#task = Task.new
end
And here is my task controller:
class TasksController < ApplicationController
def create
#stream = Stream.find(params[:stream_id])
#stream.tasks.create!({:title => params[:task][:title], :user_id => 1, :owner => 1})
if #stream.save
flash[:success] = "Task created succesfully!"
else
flash[:error] = "Error creating task"
end
redirect_to #stream
end
end
Looks pretty basic to me. The problem is when it executes tasks.create, I get the following error message: "Validation failed: User can't be blank, Owner can't be blank"
What am I doing wrong?
edit: adding model code from comment
class Stream < ActiveRecord::Base
attr_accessible :title
belongs_to :user
has_many :tasks, :dependent => :destroy
validates :title, :presence=> true, :length => { :maximum =>50 }
validates :user_id, :presence => true
end
class Task < ActiveRecord::Base
attr_accessible :title
belongs_to :stream
validates :title, :presence=> true, :length => { :maximum =>70 }
validates :user_id, :presence => true
validates :owner, :presence => true
validates :stream_id, :presence => true
default_scope :order => "updated_at"
end

You should set your user_id and owner fro STREAM object
class TasksController < ApplicationController
def create
#stream = Stream.find(params[:stream_id])
#stream.tasks.create!({:title => params[:task][:title], :user_id => 1, :owner => 1})
#stream.user_id = 1
#stream.owner = 1
if #stream.save
flash[:success] = "Task created succesfully!"
else
flash[:error] = "Error creating task"
end
redirect_to #stream
end
end

Unfortunately i can't test my suggestion currently but you might have to add
Attr_accessible :user,:owner
To the task model because you are mass-assigning these field using the hash.

Related

submitting accepts_nested_attributes and failed validations in parent

Thanks in advance for your help.
This may get a little long.
Setup
Models:
class Collection < ActiveRecord::Base
has_many :collection_ownerships
has_many :items
has_many :users, :through => :collection_ownerships
validates :title, presence: true, length: { maximum: 25 }
validates :description, length: { maximum: 100 }
end
class Item < ActiveRecord::Base
belongs_to :collection
has_many :item_ownerships, :dependent => :destroy
accepts_nested_attributes_for :item_ownerships
validates :name, :presence => true, length: { maximum: 50 }
validates :collection, :presence => true
end
class ItemOwnership < ActiveRecord::Base
belongs_to :item
belongs_to :user
validates :user, :presence => true
validates :item, :presence => true
end
Controllers
class ItemsController < ApplicationController
before_action :authenticate_user!
before_filter(:except => :toggle_item_owned_state) do
#collection = current_user.collections.find(params[:collection_id])
#item_list = #collection.items
end
def toggle_item_owned_state
#item = Item.find_by_id(params[:item_id])
#io = #item.item_ownerships.find_by_user_id(current_user)
#result = #io.update_attributes(:owned => #io.owned? ? false : true)
respond_to do |format|
if #result
format.html { }
format.js {}
else
format.html { }
format.js {}
end
end
end
def index
end
def new
#item = #collection.items.new
#item_ownership = #item.item_ownerships.build(:owned => true, :user => current_user, :item => #item)
end
def create
#item ||= #collection.items.new(item_params)
#item_ownership ||= #item.item_ownerships.build(:user => current_user, :item => item)
if #item.save
redirect_to collection_items_path(#collection)
else
flash.now[:alert] = "There was a problem saving this item."
render "new"
end
end
def edit
#item = #collection.items.find_by_id(params[:id])
end
def update
#item = #collection.items.find_by_id(params[:id])
if #item.update_attributes(item_params)
redirect_to collection_items_path(#collection)
else
flash.now[:alert] = "There was a problem saving this item."
render "edit"
end
end
def item_params
params.require(:item).permit(:name,
item_ownerships_attributes: [:id, :owned ])
end
end
View
<div class="row">
<span class="col-sm-12">
<div id="add_item">
<%= form_for [#collection, #item] do |f| %>
<div class="form-group <%= 'has-error has-feedback' if #item.errors[:name].present? %>">
<label class="sr-only" for="item_name">Item Name</label>
<%= f.text_field :name, :autofocus => true, :placeholder => "Item Name", :class => "form-control", :'aria-describedBy' => "itemNameBlock" %>
<% if #item.errors[:name].present? %>
<span id="itemNameBlock" class="error">Item <%= #item.errors[:name].first %></span>
<% end %>
<%= f.fields_for :item_ownerships do |io| %>
<%= io.check_box :owned %> Owned
<% end %>
</div>
<div id="signin_button_row">
<%= f.submit "Save", :class => "form-control green_button" %>
<span id="forgot_my_password" class="right-justify">
<%= link_to "cancel", collection_items_path(#collection), :class => "new_colors terms" %>
</span>
</div>
<% end %>
</div>
</span>
</div>
Symptom of problem:
When I submit the form in a failure situation (ie name is not provided), the form currently detects the validation and displays the error, however, I now get two checkboxes called 'owned' appearing in the form. Each failing submission adds an additional checkbox (as per attached image).
Can anyone help?
Update
This issue has taken a turn for the strange. I haven't changed anything (I know, I know, you don't believe it) other than restarting the server but now nothing is saved even with valid data. I am getting validation errors saying:
Validation failed: Item ownerships user can't be blank, Item ownerships item can't be blank
In your controller create action you have
def create
#item ||= #collection.items.new(item_params)
#item_ownership ||= #item.item_ownerships.build(:user => current_user)
if #item.save
redirect_to collection_items_path(#collection)
else
flash.now[:alert] = "There was a problem saving this item."
render "new"
end
end
Problem is on third line when you assign #item_ownership this variable is always empty at this time so code after = is executed. #item.item_ownerships.build(:user => current_user) always build new item_ownership and stores it into #item instance. So after first call of create you will have two item_ownerships first is from form and second is newly created. If validation fails and you send form again you will have three instances of item_ownerships two from the form a one newly created etc.
#edariedl was on the right track in addressing the initial response. The second line is completely unnecessary.
The second issue mentioned in the update I addressed in a separate question found here: Losing mind over validation failure saving nested models

rails saving has_(and_belongs_to_)many relationship using :through

So i want to save a HABTM relationship but i have extra field on my model so i'm usign has_many and through method.
Here's my models:
#project_task.rb
class ProjectTask < ActiveRecord::Base
attr_accessible :description, :name, :user_id, :project_id, :user_ids
belongs_to :project
belongs_to :user #Created by
has_many :project_task_users #Here is the HABTM
has_many :users, :through => :project_task_users #AND HERE
validates :name, :presence => true
validates :project_task_users, :length => { :minimum => 1} #must have atleast 1 record in the HABTM relation
accepts_nested_attributes_for :project_task_users
end
#project_task_user.rb
class ProjectTaskUser < ActiveRecord::Base
belongs_to :user
belongs_to :project_task
end
My form:
<p>Users:</p>
<% for user in #users %>
<div>
<%= check_box_tag "project_task[user_ids][]", user.id, #task.users.include?(user) %>
<%= user.name %> - <%= user.company.name %>
</div>
<% end %>
My controller:
GET
def new_task
#project = Project.find(params[:project_id])
#task = ProjectTask.new(:project_id => #project.id)
#users = #project.users
end
POST
def new_task_post
#project = Project.find(params[:project_id])
#task = ProjectTask.new params[:project_task]
#task.user_id = current_user.id
if #task.save
redirect_to #project
else
#users = #project.users
render action:"new_task"
end
end
When i submit, the #task.save returns false and the errors array with the project_task_users validation
Update
as Pablo89 pointed out i renamed my check_box_tag to this
<%= check_box_tag :user_ids, user.id, #task.users.include?(user), :name => 'project_task[user_ids][]' -%>
and it saves only if i comment out the validation. How should i validate that a user is selected while creating a project_task?

Rails 3.1: Nested attributes won't save through the form

I suspect this might be a very simple mistake but I've spent 3 hours looking for it so I thought I might ask for some help from the community.
I'm running through Ryan Bates' excellent screencasts on Nested Models Forms and trying to apply them to my own project. The problem is the nested attribute doesn't seem to save using the form. I can get it to save through the console but it only shows up as empty brackets when going through the form.
Here's the relevant code:
The form view (using haml)
= form_for(#article) do |f|
- if #article.errors.any?
#error_explanation
%h2
= pluralize(#article.errors.count, "error")
prohibited this article from being saved:
%ul
- #article.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :title
%br/
= f.text_field :title
.field
= f.label :intro
%br/
= f.text_area :intro
= f.fields_for :subsections do |builder|
= render 'subsections_fields', :f => builder
.field
= f.label :published_at
%br/
= f.text_field :published_at
.actions
= submit_or_cancel(f)
subsection_fields form view
= f.label :header
%br/
= f.text_field :header
= f.label :order_id
= f.number_field :order_id
%br/
= f.label :body
%br/
= f.text_area :body
%br/
= f.check_box :_destroy
= f.label :_destroy, "Remove Subsection"
%br/
Controller
class ArticlesController < ApplicationController
def new
#article = Article.new
3.times { #article.subsections.build }
end
def create
#article = Article.new(params[:article])
if #article.save
flash[:notice] = "Successfully created article."
redirect_to #article
else
render :action => 'new'
end
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
if #article.update_attributes(params[:article])
flash[:notice] = "Successfully updated article."
redirect_to #survey
else
render :action => 'edit'
end
end
def destroy
Article.find(params[:id]).destroy
flash[:notice] = "Succesfully destroy article."
redirect_to articles_url
end
def show
#article = Article.find(params[:id])
end
def index
#articles = Article.all
end
end
And the models
class Article < ActiveRecord::Base
attr_accessible :title, :intro
has_many :subsections, :dependent => :destroy
accepts_nested_attributes_for :subsections, :reject_if => lambda { |a| a[:body].blank? },
:allow_destroy => true
has_and_belongs_to_many :categories
validates :title, :presence => true
end
class Subsection < ActiveRecord::Base
attr_accessible :header, :body, :order_id
belongs_to :article
validates :header, :presence => true
validates :body, :presence => true
end
Any help figuring this out is much appreciated.
I'm not quite sure, but try it with attr_accessible :article_id as well in your Subsection model?
Adding "attr_accessible" to a model changes the way mass assignment works in rails.
If you remove the "attr_accessible" lines in your models then all your code will work perfectly as it is.
The class method "accepts_nested_attributes_for" adds a "subsections_attributes=(value)" method to your model.
The second you add "attr_accessible" to a model you now are forced into adding extra "attr_accessible" entries for each field that you want to assign via mass assignment. i.e. when you use Article.new(params[:article]).
I hope that was clear.
I figured out the answer to this one from another question.
The answer was to set my subsections_attributes as an attr_accessible, so the above Article model should look like this (I also added published_at as an attr_accessible):
class Article < ActiveRecord::Base
attr_accessible :title, :intro, :subsections_attributes, :published_at
has_many :subsections, :dependent => :destroy
accepts_nested_attributes_for :subsections, :reject_if => lambda { |a| a[:body].blank? },
:allow_destroy => true
has_and_belongs_to_many :categories
validates :title, :presence => true
end

Using .build method to create through 1to1 association

I have a one to one relationship with a simple users model and a profiles model:
models/user
class User < ActiveRecord::Base
authenticates_with_sorcery!
attr_accessible :email, :password, :password_confirmation
has_one :profile, :dependent => :destroy
validates_presence_of :password, :on => :create
validates :password, :confirmation => true,
:length => { :within => 6..100 }
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => {:case_sensitive => false},
:length => { :within => 3..50 }
end
models/profile
# == Schema Information
#
# Table name: profiles
#
# id :integer not null, primary key
# weight :decimal(, )
# created_at :datetime
# updated_at :datetime
#
class Profile < ActiveRecord::Base
attr_accessible :weight
belongs_to :user
end
I am doing it this way because I would like users to be able to track weight over time as well as store other more static data like height in profiles.
However, my new and create methods don't seem to be working correctly. I on submit of the new action I get this error:
undefined method `build' for nil:NilClass
profile_controller
class ProfilesController < ApplicationController
def new
#profile = Profile.new if current_user
end
def create
#profile = current_user.profile.build(params[:profile])
if #profile.save
flash[:success] = "Profile Saved"
redirect_to root_path
else
render 'pages/home'
end
end
def destory
end
end
and profile view for new
<%= form_for #profile do |f| %>
<div class="field">
<%= f.text_field :weight %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
Thanks in advance for any help you might be able to give. Noob here!
The build syntax for has_one association is different from has_many association.
Change your code as follows:
#profile = current_user.build_profile(params[:profile])
Reference: SO Answer

How can i make multiple model associations through an html form?

I would like to create pages for Discussions on a website, and on those discussion pages have users be able to write posts. The posts need to belong to the discussion and a user, and the discussion to a user.
I have thus created two models, two controllers, and one partial to put on the discussion show page. Note that the redirects from the controllers are just assigned to root_pages and others in no logical fashion, as I wanted to deal with redirects once I got the form working. I didn't attach the user model, as it is long and I didn't think it was necessary.
My problem is that I can't get the post controller to assign the correct discussion id to a new post. I'd like for this to be recorded, so that posts are associated to the author user_id (which works) and the discussion_id. I know that using #post.discussion_id = #discussion.id won't assign this properly, but I have tested #post.discussion_id = 1 to see if the rest of the code works (it does).
How do I change the set-up of the forms/controllers to assign the discussion_id here? Any help would be much appreciated!
Discussion Controller:
class DiscussionsController < ApplicationController
def show
#discussion = Discussion.find(params[:id])
#title = #discussion.title
#post = Post.new if signed_in?
end
end
Discussion Model:
class Discussion < ActiveRecord::Base
attr_accessible :title, :prompt
belongs_to :user
validates :title, :presence => true, :length => { :within => 5..100 }
validates :prompt, :presence => true, :length => { :within => 5..250 }
validates :user_id, :presence => true
has_many :posts, :dependent => :destroy
default_scope :order => 'discussions.created_at DESC'
end
Post Controller:
class PostsController < ApplicationController
def create
#post = current_user.posts.build(params[:post])
#post.discussion_id = #discussion.id
if #post.save
redirect_to discussion_path
else
redirect_to user_path
end
end
end
Post Model:
class Post < ActiveRecord::Base
attr_accessible :content
validates :content, :presence => true, :length => { :maximum => 10000 }
validates :user_id, :presence => true
validates :discussion_id, :presence => true
belongs_to :user
belongs_to :discussion
default_scope :order => 'posts.created_at ASC'
end
Partial for post form:
<%= form_for #post do |f| %>
<%= render 'shared/error_messages' %>
<div class="field">
<%= f.text_area :content, :class => "inputform largeinputform round" %>
</div>
<div class="actions">
<%= f.submit "Post", :class => "submitbutton round" %>
</div>
<% end %>
Your issue is that you do not have a mechanism to get the #discussion into the post controller One approach might be to put the discussion id in a hidden field in your partial form and then read it in your controller as a param.
Lance
in PostsController you're not creating a #discussion in create method.

Resources