Ok, I've searched high and low, read tutorials, watched videos and I am still not getting any where with this. I've read similar questions here, but questions were more complex or lacked answers - so here goes...
I have models Account and Invoice. When showing an Account, I'd like a link to 'Create new invoice' which relates to that account. (Later I'd actually like a select field to choose an Account when creating an Invoice, but I'll leave that to another excruciation).
Here are my models...
Account:
class Account < ActiveRecord::Base
attr_accessible :name, :invoice
attr_accessible :name, :invoice
has_many :invoices
end
and Invoice:
class Invoice < ActiveRecord::Base
belongs_to :account
attr_accessible :amount_pretax, :amount_total, :date_sent, :project, :status, :tax, :account, :account_id
end
Now, in my /views/accounts/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
<%= #account.name %>
</p>
<%= link_to 'New Invoice', new_invoice_path(:account_id=>#account.id) %>
<%= link_to 'Edit', edit_account_path(#account) %> |
<%= link_to 'Back', accounts_path %>
So, what's happening is, when I click on the New Invoice link it shows the new form, with the account field populated with this weird text: #<Account:0x10fe16bc0> and then when I submit the form I get this error:
ActiveRecord::AssociationTypeMismatch in InvoicesController#create
with this statement: Account(#2281084000) expected, got String(#2267210740)
along with this:
app/controllers/invoices_controller.rb:45:in `new'
app/controllers/invoices_controller.rb:45:in `create'
This is what is in the Invoices Controller:
def new
#invoice = Invoice.new(:account_id => params[:account_id])
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #invoice }
end
end
def create
#invoice = Invoice.new(params[:invoice])
....
end
The above is where I think I'm going wrong, but what to put this those lines is beyond me at the moment. I'm totally a beginner, any help to solve this functionality will surely teach me loads.
Thanks for your time.
When you click the New invoice link on the /views/accounts/show page, I suppose that you want that your new invoice belongs to this account.
So in your form, you don't have to let the user choose an account. You can for example replace the corresponding field by a hidden_field:
<%= f.hidden_field :account_id, :value => params[:account_id] %>
Also in the new action of your controller, replace #invoice = Invoice.new(:account_id => params[:account_id]) by #invoice = Invoice.new
Hope this helps.
you did not post the code of your form, but i guess that you are using a text-field for handling the account association. THIS IS WRONG!
if you use a text-field, then rails will try storing it as a string => Account(#2281084000) expected, got String(#2267210740)
you need to use some kind of relational field like a dropdown or whatever to select one of the accounts that are already there.
there are tons of good examples out there, this might help you: http://railscasts.com/episodes/102-auto-complete-association-revised
Related
I am creating a small app to manage my own online portfolio but I can't solve an issue with update.
The main table of my database is works, then there's the tables authors and clients where I set the fields authorName and clientName as unique. Authors and clients can have multiple works, but a work can only have one of each.
In the form where I create works I have a field for the authorName and another one for clientName: if the author doesn't exist in the authors table it gets created, otherwise the existing one gets linked to the work. Same thing with the client. To achieve this I am using first_or_initialize and it works perfectly. Problems start when I try to use the same method inside the update action. These are my models:
Work Model
class Work < ApplicationRecord
has_one :description
belongs_to :author
belongs_to :client
accepts_nested_attributes_for :client
accepts_nested_attributes_for :author
scope :active, lambda {where(:isActive => true)}
scope :descOrder, lambda {order(:date => :desc)}
scope :cover, lambda {where(:isCover => true)}
end
Author Model
class Author < ApplicationRecord
has_many :works
end
Client Model
class Client < ApplicationRecord
has_many :works
end
Quick edit: the models as you see them are exactly as they are in my app. No code has been removed.
This is the form in my view:
<%= form_for(#work, :url => { :controller => "projects", :action => "update"} ) do |f| %>
<%= f.label("title") %>
<%= f.text_field(:title) %>
<%= f.fields_for(:author) do |author| %>
<%= author.label("author") %>
<%= author.text_field(:authorName) %>
<% end %>
<%= f.fields_for(:client) do |client| %>
<%= client.label("client") %>
<%= client.text_field(:clientName) %>
<% end %>
<%= f.label("date") %>
<%= f.date_field(:date) %>
<%= f.submit("update") %>
<% end %>
And this is how I am handling it in the controller:
def edit
#work = Work.find(params[:id])
if #work.client.nil?
#work.build_client
end
if #work.author.nil?
#work.build_author
end
end
def update
#work = Work.find(params[:id])
#work.client = Client.where(clientName: work_params["client_attributes"]["clientName"]).first_or_initialize
#work.author = Author.where(authorName: work_params["author_attributes"]["authorName"]).first_or_initialize
if #work.update(work_params)
flash[:notice] = "work: #{#work.title} updated successfully."
redirect_to(project_path(#work))
else
redirect_to new_project_path
end
end
private
def work_params
params.require(:work).permit(:title, :date, client_attributes: [:id, :clientName], author_attributes: [:id, :authorName])
end
This is the error that I get:
Started PATCH "/projects/21" for ::1 at 2019-10-14 20:00:27 -0700
Processing by ProjectsController#update as HTML
Parameters: {"authenticity_token"=>"rw3X7VJg8CDnOibniv1jKHTVTGp7pjE4ep6xHpHy0Zp8Xv/0uQd6y5xqq629M2FOOQNoYyOAXH//w5/VoeNEOA==", "work"=>{"title"=>"Progetto1", "author_attributes"=>{"authorName"=>"Autore1", "id"=>"34"}, "client_attributes"=>{"clientName"=>"Cliente4", "id"=>"30"}, "date"=>""}, "commit"=>"update", "id"=>"21"}
Work Load (0.7ms) SELECT `works`.* FROM `works` WHERE `works`.`id` = 21 LIMIT 1
↳ app/controllers/projects_controller.rb:42:in `update'
Client Load (0.6ms) SELECT `clients`.* FROM `clients` WHERE `clients`.`clientName` = 'Cliente4' ORDER BY `clients`.`id` ASC LIMIT 1
↳ app/controllers/projects_controller.rb:44:in `update'
Author Load (0.6ms) SELECT `authors`.* FROM `authors` WHERE `authors`.`authorName` = 'Autore1' ORDER BY `authors`.`id` ASC LIMIT 1
↳ app/controllers/projects_controller.rb:45:in `update'
Completed 404 Not Found in 15ms (ActiveRecord: 1.9ms | Allocations: 3805)
ActiveRecord::RecordNotFound (Couldn't find Client with ID=30 for Work with ID=21):
app/controllers/projects_controller.rb:47:in `update'
Even though those record do exist in the database (with those IDs that you see in the error) and their foreign keys are correctly stored in the works table (I checked in mysql).
What I am expecting to achieve is the same behaviour of the new action (that I described in the beginning of my post).
How can I solve this? Thank you!
Small update: if I change first_or_initialize with first_or_create it does create the author (or client), if not existing, but in the same time it still gives me the same error.
After dozens of tests I eventually came up with a sort of solution - it's not "elegant" and probably it's not the best, but at least it works without interruptions:
def edit
#work = Work.find(params[:id])
if #work.client.nil?
#work.build_client
end
if #work.author.nil?
#work.build_author
end
end
def update
#work = Work.find(params[:id])
#work.client = Client.where(clientName: work_params["client_attributes"]["clientName"]).first_or_create
#work.author = Author.where(authorName: work_params["author_attributes"]["authorName"]).first_or_create
if #work.update(update_params)
flash[:notice] = "work: #{#work.title} updated successfully."
redirect_to(project_path(#work))
else
redirect_to new_project_path
end
end
private
def work_params
params.require(:work).permit(:title, :date, client_attributes: [:id, :clientName], author_attributes: [:id, :authorName])
end
def update_params
params.require(:work).permit(:title, :date)
end
Being that first_or_initialize works smoothly with save method, but it gets stuck - at least in my code - with update, I replaced it with first_or_create.
This time, however, I am using another private method update_params which simply ignores :client_attributes and :authors_attributes.
This results in an Unpermitted parameters: :author_attributes, :client_attributes error, as expected, but at least everything goes through and gets updated.
Honestly I am not quite sure that I totally understood why it's working. But it does.
Anyway, if anyone have a better solution to this problem I am totally open to improve it.
I'm currently trying to create a survey which will use questions that are stored in a table. I've read nested model form part 1 from rails casts however i'm not getting anywhere as the questions are not displaying in the survey.
I have three tables, one tables has the text of the questions, another table keeps the record of who entered the survey and a third table which keeps the answers from a user for the questions.
variable table:
name: varchar
id: integer
report table
employee name: varchar
date: date
id: integer
report_variable table
question_id
report_id
answer
Code i modified for reports/new:
# GET /reports/new
# GET /reports/new.json
def new
#report = Report.new
#variable = #report.variable.build #dont know what to do here, gives an error with report_id
respond_to do |format|
format.html # new.html.erb
format.json { render json: #report }
end
end
modified report/_form.html.erb
<div >
<%= f.fields_for :variable do |builder| %>
<%= render variable_fields, :f => builder %>
<% end %>
</div>
created report/_variable_fields.html.erb
<p>
<%= f.label :name %>
<%= f.text_field :name, :class => 'text_field' %>
<p>
model for report_variable
class ReportVariable < ActiveRecord::Base
attr_accessible :report_id, :value, :variable_id
has_and_belongs to many :reports
has_and_belongs to many :variables
end
model for report
class Report < ActiveRecord::Base
attr_accessible :employeeName
has_many :report_variable
has_many :variable
accepts_nested_attributes_for :report_variable
accepts_nested_attributes_for :variable
end
Sorry if it's a simple question, im pretty new to rails.
Welcome to Rails!
I think the simple answer is the fields aren't showing up because there aren't any nested records. You can probably get around that by uncommenting the variable line as you have it:
def new
#report = Report.new
#report.variables.build #this line creates 1 new empty variable, unsaved.
respond_to do |format|
format.html # new.html.erb
format.json { render json: #report }
end
end
If you want more than one variable, call something like:
3.times { #report.variables.build }
That way the #report object you're placing in the form helper will have three variables on it. This should get you moving again, the harder thing is going to be adding ajax addition / removal of variables, but if you know how many there are in advance you don't have to deal with that.
Good luck!
I have tried to get this working, looked at multiple tutorials, questions on here tried different things for about a week now and I can't get the view to work correctly.
I have teams of users. A team has_many users and a user belongs_to a team (one team at a time). I know the association works because I got it working using the console (with some help there). I'm not sure how to get it working in the view. Below is the code, please let me know if more is needed.
What am I missing?
_join_team_button
<%= form_for(#user) do |f| %>
<%= f.submit "Join Team", class: "btn btn-large btn-primary" %>
<% end %>
Team Show Page
<%= render 'shared/join_team_button %>
Teams Controller
def show
#team = Team.find(params[:id])
#team_members = #team.users
#user = current_user.users.build if signed_in?
end
Users Controller
def show
#user = User.find(params[:id])
#teams = #user.team
end
I tried to put a complete demonstration of what you are looking for. Let me know if it fits for you.
#FILE: models/team.rb
class Team < AR::Base
has_many :users
end
#FILE: models/user.rb
class User < AR::Base
belongs_to :team
end
#FILE: config/routes.rb
#Here you are defining "users" as a nested resource of "teams"
resources :teams do
resources :users do
member do
put :join
end
end
end
#if you run "rake routes" it will show you the following line along with others
join_team_user PUT /teams/:team_id/users/:id/join(.:format) users#join
#FILE: controllers/team_controller.rb
def show
#team = Team.find(params[:id])
#team_members = #team.users
#user = current_user.users.build if signed_in?
end
#FILE: views/teams/show.html.erb
<% if(#user) %>
<%= form_for #user, :url => join_team_user_path(#team, #user) do |f| %>
<%= f.submit "Join Team", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
#You dont really need a form for this. You can simply use `link_to` like below
<%= link_to 'Join', join_team_user_path(#team, #user), method: :put %>
#FILE: controllers/users_controller.rb
def join
# params[:id] => is the user_id
#user = User.find(params[:id])
# params[:team_id] => is the team_id
#team = Team.find(params[:team_id])
# Now make the relationship between user and team here.
#user.update_attribute(:team, #team)
end
Update:Based on your comment
Q: Do I create a new user's resource and nest that or do I nest the already establishes user's resource?
Ans: Based on your requirements any resource can be defined both independently or nestedly. But yes you can control that which method will be available in which way. Like in your case, you can allow only join method when your user is nested under team resource.
resources :users, :only=>:join do
member do
put :join
end
end
resource :users
run rake routes with and without :only=>:join option and see differences in available routes.
Q: Will that affect other things?
Ans: If you strictly define your routes following above example, it should not affect other things. You should confirm all the available routes to your application by rake routes.
Q: Should I put my current routes.rb file up there?
Ans: Assuming your current routes.rb will be modified in the above way. Could I answer the question?
Q: Confused about the comments controller?
Ans: Im extreamely sorry. Yes it must be users_controller.rb as the rake routes command is showing. Result of copy and paste from my own example code :P
Q: what should I put there? the build method
Ans: In your case both the user and team is already exists in your database. All you need to do is just setup a relationship. So you can just use update_attribute option. Ive changed the join method. Please check. But yes if want to create new entries you might need build methods.
Sorry for the late reply :)
I figured it out. Still not perfect, but it gets the association working. The team, and the user were already created, I just needed to establish the association, so the build method would not have worked. Here's what I have:
View:
<%= form_for(#user) do |f| %>
<%= f.hidden_field :team_id, :value => #team.id %>
<%= f.submit "Join Team", class: "btn btn-large btn-primary" %>
<% end %>
Teams Controller:
def show
#team = Team.find(params[:id])
#team_members = #team.users
#user = User.find(params[:id]) if signed_in?
end
I have found a lot of posts about this Railscast but all the suggestions haven't helped me.
I have been able to render in the view a nested form field, but only one, not the 3 that I have called out in my controller. When I submit, I get the error: Can't mass-assign protected attributes: clue
Chapter.rb
class Chapter < ActiveRecord::Base
belongs_to :trail
has_many :clues, :dependent => :destroy
accepts_nested_attributes_for :clues
attr_accessible :asset, :assetkind, :description, :gate, :name, :trail, :trail_id, :cover
.
.
.
end
Clue.rb
class Clue < ActiveRecord::Base
attr_accessible :chapter_id, :theclue, :typeof, :chapter
.
.
.
belongs_to :chapter
end
In the railcast it says to use the equivalent of :clues, and this renders 3 fields. But in mine, it didn't render the fields. Instead, I use #chapter.clues and it renders only one.
My form when making a new chapter.
<h1>Add a New Chapter</h1>
<h3>Add To Trail : <%= #trail.title %></h3><br>
<%= form_for [#trail, #trail.chapters.build] do |f| %>
<h6>About the Chapter</h6>
<%= f.label :name, 'Chapter Name' %>
.
.
.
<h6>Progressing the Story</h6>
<%= f.fields_for #chapter.clues do |builder| %>
<p>
<%= builder.label :theclue, "Enter Clue" %>
<%= builder.text_area :theclue, :rows => 2 %>
</p>
<% end %>
.
.
.
<% end %>
My chapters_controller.rb new
class ChaptersController < ApplicationController
def new
#trail = Trail.find(params[:trail_id])
#chapter = Chapter.new
#title = "Chapter"
3.times { #chapter.clues.build }
logger.debug "CHAPTER!!!!!!!!!!!!new: am i in a trail? #{#trail.to_yaml}"
logger.debug "CHAPTER!!!!!!!!!!!!new: am i in a clue? #{#chapter.clues.to_yaml}"
end
My log shows me 3 clues, but the attributes are empty (no :id). Is this a sign of something wrong? So even though my log shows 3 clue objects, my view only shows one.
Thoughts? I have already, thanks to suggestions on stackoverflow, added to chapter.rb
attr_accessible :clues_attributes
and had no luck, same behavior and errors with and without that.
Thanks in advance for your time
I figured it out for myself. Not sure why exactly, I will speculate, someone is welcome to explain it better if I'm off.
The issue was here:
<%= form_for [#trail, #trail.chapters.build] do |f| %>
which I changed to:
<%= form_for #chapter do |f| %>
and then I had to change some things around in my chapters_controller to make the trails objects and capture the ids. But, after I made this change, my 3 clue fields started showing up in the view and my error about mass-assign went away.
I think the chapter I created before was empty and not really generated, only holding information, so trying to hold nested information with the clues form_for was another step of temporary data...Where as creating the object in my controller and then filling it with the form was more substantial....I know really technical...Like I said, I got it working, don't ask me how...but I'm beginning to understand how Rails thinks.
When I submit, I get the error: Can't mass-assign protected attributes: clue
This is telling you that the attribute is protected from mass assignment. Basically, the only way you would be able to set it is through a method in your code, not from user input. (Which get assigned, usually, through update_attributes on the model.)
What you need to do is add :clue to attr_accessible in models/chapter.rb.
You may want to add :clues as well - I think it should actually be giving you the error that :clues is protected. You may run into an issue with :clue_ids. Whatever it says is protected, just put in the attr_accessible method in that model and you should be able to update it from user input.
Following up on a previous question, I have a few issues to resolve before I have a comment form showing and submitting securely on my profile. I'm a beginner to programming so thinking across multiple controllers seems to have me lost.
What I'm doing is posting comments in a form, then listing them.
Background: The _comment_form and _comment reside as partials in the Profile about. (My next task is toggling from about to other Profile information, but that's another question altogether.)
Using the help provided in my last question I feel like I'm almost there but am getting an error.
CreateComments migration:
t.integer :profile_id
t.integer :author_id
t.string :body
My Comment model:
class Comment < ActiveRecord::Base
belongs_to :profile
belongs_to :author, :class_name =>"User", :foreign_key => "author_id"
end
CommentsController:
def create
#comment = Comment.new(params[:comment].merge(:author_id => current_user.id))
#comment.save!
redirect_to profile_path(#comment.profile)
end
ProfilesController:
def create
#profile = Profile.new(params[:profile])
if #profile.save
redirect_to profile_path(#profile), :notice => 'User successfully added.'
else
render :action => 'new'
end
end
def show
#user = User.find(params[:id])
#profile = #user.profile
#comment = #profile.comments.new
end
Comment partials inside Profile partial:
<div id="commentEntry">
<%= render :partial => 'comment', :collection => #profile.comments %>
</div>
<div id="newitem">
<%= render :partial => 'comment_form' %>
</div>
Routes.rb:
resources :users do
resources :profiles
end
resources :comments
_comment_form.html.erb:
<%= form_for #comment do |f| %>
<%= f.text_field :body %>
<%= f.submit 'Add new' %>
<% end %>
_comment.html.erb:
<li class="comment" title="<%= #comment.author.profile.first_name %> <%= #comment.author.profile.last_name %>">
<%= #comment.body %>
</li>
So, Issue #1: Wrapping the _comment.html.erb in a loop <% for #comment in #user.profile.comments %> shows the profile but when I try and submit a new comment I get "Unknown action The action 'update' could not be found for CommentsController". If I take away the loop, the profile doesn't show and I get "NoMethodError in Profiles#show undefined method `profile' for nil:NilClass". Can anyone help me out and explain what I'm doing wrong?
Issue #2: I created a sample comment in rails console and when I get the profile to show, the input field for comment :body repopulates with the comment's body. Any ideas on what could be going on?
Short explanation of your problem:
The #comment you're getting in your _comment_form partial is one that's already saved in your database, hence the call to the update action and the body that's already filled.
You're creating the new comment just fine with #comment = #profile.comments.new in your show action, but it gets overridden somewhere else.
You're mentioning that you wrapped the _comment render in a loop with <% for #comment in #user.profile.comments %>, the problem is most likely there.
Fix:
The only thing you should have to change is the _comment partial to (without the for loop that you added):
<li class="comment" title="<%= comment.author.profile.first_name %> <%= comment.author.profile.last_name %>">
<%= comment.body %>
</li>
When you do the render :partial => 'comment', :collection => #profile.comments, rails is smart enough to loop over #profile.comments and give the comment (not #comment) variable to the partial.
How to avoid this the next time:
I'll give you two rules of thumb to avoid getting in this situation:
Try to name your variables more precisely. #new_comment would have been a better name for the variable to store the new comment. #comment is a bit ambigous as you've got a boatload of those in your view.
Avoid creating and modifying instance variables (# variables) in your views, try to do this only in your controller. I'll admit your particular case was a bit harder to detect because of the <% for #comment in #user.profile.comments %>. The view got its name for a good reason, it's only supposed to let you view the data you've defined in your controller.
Hope this helps.