Rails: create action for associated Models - ruby-on-rails

Pretty basic Rails question I assume but I can't figure out the simplicity of Rails.
My simple goal is to have a user submit a form indicating the amount of a donation, and have that donation linked to the user. I'm having trouble with the create action in the donations_controller.rb
I have a User.rb model and a Donation.rb model. The User.rb has_one :donation and Donation.rb belongs_to :user. I am also using Devise so I have the current_user method.
My donation table looks like this
class CreateDonations < ActiveRecord::Migration
def change
create_table :donations do |t|
t.integer :amount
t.integer :user_id
t.timestamps
end
add_index :donations, [:user_id, :created_at]
end
end
The _form.html.erb for the donations_controller.rb looks like this
<%= form_for #donation do |f| %>
<div class="field">
<%= f.number_field :amount %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
The create action in donations_controller.rb looks like this
def create
#donation = current_user.donation.build(params[:donation])
if #donation.save
flash[:success] = "Donation"
redirect_to root_path
else
render 'home/index'
end
end
I'm getting this error message when I submit the form.
NoMethodError in DonationsController#create
undefined method `build' for nil:NilClass

The method for building the has_one association is current_user.build_donation(params[:donation])

Related

Ruby on Rails - NoMethodError in Signups#new

I'm getting this error while trying to pass "Innovation Cloud" lesson on Codecademy. I couldn't find any solution on Stack Overlow or GitHub.
NoMethodError in Signups#new
Showing /home/ccuser/workspace/learn-rails_innovation-cloud/innovation-cloud/app/views/signups/new.html.erb where line #40 raised:
undefined method `email' for #<Signup id: nil, created_at: nil, updated_at: nil>
Important fragments of code:
routes.rb:
Rails.application.routes.draw do
get "/thanks" => "pages#thanks"
resources :signups
root "signups#new"
end
new.html.erb
<%= form_for(#signup) do |f| %>
<div class="field">
<%= f.label :signup %><br>
<%= f.text_area :email %>
</div>
<div class="actions">
<%= f.submit "Create" %>
</div>
<% end %>
db/migration
class CreateSignups < ActiveRecord::Migration
def change
create_table :signups do |t|
t.string :email
t.timestamps
end
end
end
signups_controller.rb
class SignupsController < ApplicationController
def new
#signup = Signup.new
end
private
def signup_params
params.require(:signup).permit(:email)
end
def create
#signup = Signup.new(signup_params)
if #signup.save
redirect_to '/thanks'
else
render 'new'
end
end
end
As the error implies, some code tried to call a method email on an instance of Signup. Its not clear what line of code corresponds to the line number 40, but my guess is its the following:
<%= f.text_area :email %>
Anytime you build a form with an ActiveRecord instance (as you do with form_for(#signup)), any input generated will expect an attribute on your model with the same name. In this case, its looking for an attribute named email on #signup. It does this so that the field can be populated with the current value of that attribute on the model. While this might seem unnecessary for a #new action, it makes perfect sense for an #edit action, and allows you to reuse the same form code for both.
Since I'm guessing you'd like to store the email along with the signup, you need to add the attributes to the Signup model with a migration like so:
rails generate migration AddEmailToSignup email:string
rake db:migrate

Passing foreign key automatically from view to controller using HABTM models

I have two models, Companies and Employees, with a many-to-many association between them.
class Company < ActiveRecord::Base
has_and_belongs_to_many :employees
end
class Employee < ActiveRecord::Base
has_and_belongs_to_many :companies
end
I have a join table :companies_employees
class CreateCompaniesEmployeesJoin < ActiveRecord::Migration
def change
create_table :companies_employees, :id => false do |t|
t.integer "company_id"
t.integer "employee_id"
end
add_index :companies_employees, ["company_id", "employee_id"]
end
end
I have a Show view for Company, which includes a form_for adding a new Employee, who I want to associate with that Company via the HABTM association:
<%= form_for :employee, :url => employees_path do |f| %>
<p>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</p>
<br>
<p>
<%= f.hidden_field :company_id, :value => #company.id %>
</p>
<p>
<%= f.submit "Save Employee", class: "btn btn-default" %>
</p>
<% end %>
I have a controller for Employee, through which I want to create a new Employee that will be automatically associated with the Company from the Company Show view:
def create
#company = Company.find(params[:company_id])
#employee = Employee.new(employee_params)
#company.employees << #employee
if #employee.save
flash[:success] = "Company Employee Added!"
redirect_to #employee
else
render 'new'
end
end
When I use the form to try to create a new employee, I get an error in EmployeeController -- "Couldn't find Company without an ID"
Seems my view is failing to pass the :company_id on to the create action in the EmployeeController.
I've scoured other posts and nothing seems to be on point. Any suggestions most appreciated!
Ok, the problem seems to be the nested attribute.
Try to change in the EmployeesController#create the first row in:
#company = Company.find(params[:employee][:company_id])
EDIT
Alternatively, and probably more easy, you can also change the form hidden_field like this:
hidden_field_tag(:company_id, #company.id)

Why doesn't my user follow/unfollow button work?

I am working on building an application (following Michael Hartl's chapter 11) where users can follow projects that are created by other users.
I created a ProjectRelationship model to hold two components: follower_id for the users and projectuser_id for the projects. The foreign keys have been set up as such.
Right now, my _follow_form.html.erb page renders "follow" or "unfollow" depending on whether the current_user is following the project. Please see my code below and see what I am missing.
Right now, the follow button is generated on each project show page. But when I click the button follow button that is generated by _follow.html.erb, it does not seem to follow the project or update the count when I call #project.followers.count as the POST is not happening.
And thus, when I click follow button, the URL becomes all jumbled. See example:
#Goes from
domain.com/projects/21
#to
domain.com/projects/21?utf8=%E2%9C%93&authenticity_token=5EQmU0EkHB5yKDYakqL78piMWzZl0CfdpHFEqBeQiN4%3D&project_relationship%5Bprojectuser_id%5D=21&commit=Follow%22
**Update:
It seems to work now, but I'm not sure if I really changed anything but got rid of the follower_id index :unique => true through a migration change.
schema.rb
create_table "project_relationships", :force => true do |t|
t.integer "follower_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "projectuser_id"
end
add_index "project_relationships", ["follower_id"], :name => "index_project_relationships_on_follower_id", :unique => true
add_index "project_relationships", ["projectuser_id"], :name => "index_project_relationships_on_projectuser_id"
routes.rb
resources :projects do
resources :comments
member do
get :following
end
end
resources :project_relationships, only: [:create, :destroy]
project_relationship.rb
class ProjectRelationship < ActiveRecord::Base
attr_accessible :projectuser_id
belongs_to :user, foreign_key: "follower_id"
belongs_to :project, foreign_key: "projectuser_id"
end
project.rb
has_many :project_relationships, foreign_key: "projectuser_id"
has_many :favorited_by, through: :project_relationships, source: :user
user.rb
has_many :project_relationships, foreign_key: "follower_id"
has_many :followed_projects, through: :project_relationships, source: :project
def following_project?(project)
project_relationships.find_by_follower_id(project.id)
end
def follow_project!(project)
project_relationships.create!(projectuser_id: project.id)
end
def project_unfollow!(project)
project_relationships.find_by_projectuser_id(project.id).destroy
end
project_relationships_controller.rb
class ProjectRelationshipsController < ApplicationController
def create
#project = Project.find(params[:project_relationship][:projectuser_id])
current_user.follow_project!(#project)
redirect_to #project
end
def destroy
#project = ProjectRelationship.find(params[:id]).followed_project
current_user.project_unfollow!(#project)
redirect_to #project
end
end
projects/show.html.erb
<%= render 'follow_form' if signed_in? %>
projects/_follow_form.html.erb
<% if current_user.following_project?(#project) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
projects/_follow.html.erb
<%= form_for(current_user.project_relationships.build(projectuser_id: #project.id)) do |f| %>
<div><%= f.hidden_field :projectuser_id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
projects/_unfollow.html.erb
<%= form_for(current_user.project_relationships.find_by_projectuser_id(#project),
html: { method: :delete }) do |f| %>
<%= f.submit "Unfollow", class: "btn btn-large" %>
<% end %>
First of all - if you run projectfollow!(project) and projectunfollow!(project) in your console (with a user, project etc) do they work properly?
For your forms try the following instead and see if it works:
<%= form_for(current_user.project_relationships.build, url: project_relationships_path(project_id: #project.id)) do |f| %>
Then in your project relationships controller:
class ProjectRelationshipsController < ApplicationController
def create
#project = Project.find(params[:project_id])
current_user.projectfollow!(#project)
redirect_to #project
end
end
So if your create URL is /project_relationships (via POST), the above should post to /project_relationships?project_id=5 and then the controller can find that project.
Also, try to rename your methods so they make sense:
def following_project?(project)
end
def follow_project!(project)
end
def unfollow_project!(project)
end
Now current_user.following_project?(project) makes a lot of sense!
Update
Ok, I think the following is the problem, in your create action you're getting the id from the params:
#project = Project.find(params[:project_relationship][:projectuser_id])
However in your form you're not setting the value of the hidden field:
<%= f.hidden_field :projectuser_id %>
Change it to the following and see if it works:
<%= f.hidden_field :projectuser_id, value: #project.id %> # or wherever the id is from
The problem was that my follow/unfollow form was embedded in another form which caused the error. Once taken out, worked!

How do you define a virtual attribute to a real models attribute and then save it?

In my application I am trying to make a virtual attribute that's a date_select in my User model that associates itself to my UserPrice model's date_select. The reason being, I am trying to have only one date dropdown list that users have to select. I plan on doing this by making the User virtual date_select update all of my UserPrice date_selects to that particular date.
I have my UserPrice model with the :purchase_date attribute:
create_table :user_prices do |t|
t.decimal :price
t.date :purchase_date
I have a nested form which can be shown here by looking at my UserPrice controller which also on create updates all of the UserPrices generated on the form:
UserPrices Controller
def add_store_prices
#a_new_user = User.new
5.times do
#a_new_user.user_prices.build
end
end
def create
respond_to do |format|
if current_user.update_attributes(params[:user])
redirect_to :back
else
redirect_to :back
end
end
end
Then I am trying to define in the User model how to update the attributes of the :purchase_dates and also get the f.date_select field to work:
class User < ActiveRecord::Base
attr_accessible :user_prices_attributes, :all_dates
has_many :user_prices
attr_writer :all_dates
def all_dates
# #all_dates = UserPrice :purchase_date field?
#Needs to update the :purchase_date of the UserPrices
end
I am not exactly sure where to start. How do I define the virtual attribute :all_dates and get it to update on create all of the UserPrice's :purchase_date attributes generated on my form?
The end result should look something like this:
<%= form_for(#a_new_user, :url => {:controller => :user_prices, :action => :create}) do |f| %>
<%= f.error_messages %>
<%= f.date_select :all_dates %>
<%= f.fields_for :user_prices do |pf| %>
<%= pf.text_field :product_name %>
<%= pf.text_field :store %>
<%= pf.text_field :price %>
<% end %>
<%= f.submit %>
<% end %>
Thanks, really need some help with this.
EDIT
This is the error I get with the following controller code:
attr_accessor :all_dates
after_save :save_all_dates_to_user_prices
protected
def save_all_dates_to_user_prices
if !self.all_dates.nil?
self.user_prices.update_all :purchase_date => self.all_dates
end
end
ActiveRecord::MultiparameterAssignmentErrors in UserPricesController#create
1 error(s) on assignment of multiparameter attributes
app/controllers/user_prices_controller.rb:30:in `block in create'
app/controllers/user_prices_controller.rb:29:in `create'
"utf8"=>"✓",
"authenticity_token"=>"fmB0kymP2vetFDoI/BB6RkyMEgsnhvf04cJ5Vu1GEaI=",
"user"=>{"all_dates(2i)"=>"10",
"all_dates(3i)"=>"17",
"all_dates(1i)"=>"2011",
"user_prices_attributes"=>{"0"=>{"product_name"=>"Cherries",
"store"=>"4255 Alafaya Trail,
Oviedo,
FL 32765,
USA",
"price"=>"2.99"},
"1"=>{"product_name"=>"",
"store"=>"",
"price"=>"5.99"},
"2"=>{"product_name"=>"",
"store"=>"",
"price"=>""},
"3"=>{"product_name"=>"",
"store"=>"",
"price"=>""},
"4"=>{"product_name"=>"",
"store"=>"",
"price"=>""}}},
"commit"=>"Done"}
You are pretty close as far as your User model goes:
class User < ActiveRecord::Base
# See: http://gabeodess.heroku.com/posts/14
composed_of :all_dates, :class_name => "DateTime",
:mapping => %w(Time to_s),
:constructor => Proc.new { |item| item },
:converter => Proc.new { |item| item }
after_save :save_all_dates_to_user_prices
protected
def save_all_dates_to_user_prices
if !self.all_dates.nil?
self.user_prices.update_all :purchase_date => self.all_dates
end
end
end
I'll leave it up to you to figure out the best way to decide when all_dates gets saved. In the example I've given, all_dates is used to update the User's prices after the User is saved (during either update or create), and only when all_dates is not nil (for example, whenever an input is provided on the form).
I assume that you've set up your User model so that it accepts_nested_attributes_for :user_prices. I don't quite understand why you are updating the current user in the create method of your UserPrices, but I don't see any problems in the code itself.

is an instance variable in an action of a controller available for all the controllers view?

I am just trying to print the parameters that have been entered into my form.
Basically I create a new bet then I display the parameters:
MIGRATION
class CreateBets < ActiveRecord::Migration
def self.up
create_table :bets do |t|
t.integer :accepted ,:default => 0
t.integer :user_1_id #proposer
t.integer :user_2_id #receiver
t.integer :team_1_id #proposer's team
t.integer :team_2_id #receiver's team
t.integer :game_id
t.integer :winner
t.integer :amount
t.timestamps
end
end
def self.down
drop_table :bets
end
end
CONTROLLER
bets_controller.erb
class BetsController < ApplicationController
def index
redirect_to new_bet_path
end
def new
#b=Bet.new
end
def create
#points=params[:points]
#winner=params[:winner]
end
end
VIEWS
New.erb
<% facebook_form_for Bet.new do |f| %>
<%= f.text_field :amount, :label=>"points" %>
<%= f.text_field :winner, :label=>"WinningTeam" %>
<%= f.buttons "Bet" %>
<% end %>
create.erb
points:<%= #points %>
<br>
winner:<%= #winner %>
I tried to make this code work with instance variables but it didn't work either. Where is the problem?
Thank you.
I think that params[:winner] and params[:point] is empty hash. Try adding this to your create.erb:
params: <%= params.inspect %>
It will display your params hash, so you will see how to get to it.
Another hint, why you are creating new object in new action and then in form you are doing it again? So:
<% facebook_form_for #b do |f| %>
And another thing, it is really good to keep naming conventions, so don't create #b object, but #bet.
In create action you should have line like this:
#bet = Bet.new(params[:bet])
And in view:
<p>
points:<%= #bet.points %>
</p>
<p>
winner:<%= #bet.winner %>
</p>
If you use <br> it's better to use <br/>.
Your index action is totaly useless. It would be better if you would move all behaviour from new action to index and remove new action completly.
As klew pointed, for me it seems that you're getting empty params[:winner]and params[:point]. You can make sure that of what you're getting by taking a look at your servers log.
You will see a line like
Processing BetsController#create (for 127.0.0.1 at 2010-04-11 20:56:51) [POST]
Parameters: {"your"=>"parameters", "should"=>"apper in this hash"}

Resources