Create Child only and select a Parent in nested form? - ruby-on-rails

Instead of creating a new Parent and also creating the children. Is it possible to select from a list of Parents and then only create the children that are assigned to a current user and that specific Survey?
Lets use this example:
class Survey < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions
end
class Question < ActiveRecord::Base
belongs_to :survey
belongs_to :user
end
And then in the controller:
def new
# #survey = select menu of all Surveys
3.times do
question = #survey.questions.build
end
end
def create
# Saves new questions with current user
if #survey.save
flash[:notice] = "Success"
redirect_to #survey
else
render :action => 'new'
end
end
I'm not sure what the create and new actions would turn into. Any idea?

You can call the edit action on a existing survey passing the selected survey to it:
edit_survey_path(#survey)
Then you can load the selected survey in that action:
def edit
#survey = Survey.find(params[:id])
end
In the edit view, use a nested form to add/delete questions, and then, in the update action, updating your surveys attributes will also add and delete the questions.
def update
#survey = Survey.find(params[:id])
#survey.update_attributes(params[:survey])
redirect_to ...
end
All of this will work assuming you've set accepts_nested_attributes_for :questions in the survey model.
My answer here is a summary of Ryan Bates' screencast on nested forms which I think you've already seen, based on the similarity of your example and his.
What I'd like to point out here is that you can achieve what you want using exactly the same code, however using the edit/update actions on your parent model instead of new/create on the child model.
Edit:
In order to assign the current user to a survey question, do the explicit assignment in the new and edit action:
def new
#survey = Survey.new
3.times do
question = #survey.questions.build(:user_id => current_user.id)
end
end
def edit
# find the preselected Survey...
#survey = Survey.find(params[:id])
# This adds a (one) new empty question, consider doing it via Javascript
# for adding multiple questions.
#survey.questions.build(:user_id => current_user.id)
end
In your form for questions, add:
<%= form_builder.hidden_field :user_id %>
Don't forget to replace form_builder with your actual form builder object.
Now all the new questions will be assigned to the current user because the current user was submitted by the form along with the other attributes for questions.

Related

How to get values in show action in rails controller

I would like to have access to the parameters in nested attributes..... Below is the code.
<%= link_to "Invoice", user_invoice_path(#user, invoice) %>
How do I access the user and invoice in the Invoice controller show action.
def show
#user = User.find(params[:user_id])
#invoice = Invoice.find(params[:id])
end
User Model:
class User < ActiveRecord::Base
has_many :invoices
end
Invoice Model:
class Invoice < ActiveRecord::Base
belongs_to :user
end
I know how it works when it is not nested.... Can anyone, please help?
If I understand correctly, you can't find the #user or #invoice in this way.
Please debug your show action and you will find params as follows,
{"action"=>"show", "controller"=>"invoices", "user_id"=>"307", "id"=>"359"}
So now write your show action something like,
def show
#invoice = Invoice.find(params[:id])
#user = User.find(params[:user_id])
end
There may be better ways to find the objects in the controller action. But this is the basic approach you will have to consider instead of what you tried.

Rails 4, add dynamic fields without nested attributes

I'm currently sitting with a problem where I am trying to add dynamic fields, and it works, however I need to created a second model for the nested attributes that are dynamically generated on the field.
I have a very easy and simple form which asks for a user's name, I want to be able to allow the user to click a button ("Add additional name") and have that field added dynamically then add another name. Can I do this using only the one model and controller, and without using nested forms?
This is what I currently have, but I do not want it working this way:
Controller
class GuestsController < ApplicationController
skip_before_filter :authenticate_user!, only: [:new, :create]
def index
#guest = Guest.all
end
def new
guest = Guest.new
#guest_form = GuestForm.new(guest)
end
def show
#guest = Guest.find(params[:id])
end
def create
guest = Guest.new
#guest_form = GuestForm.new(guest)
#guest_form.submit(guest_params)
if #guest_form.save
respond_to do |format|
format.html { redirect_to :back, notice: 'Thank you for replying' }
format.js
end
else
respond_to do |format|
format.html { render :new }
format.js
end
end
end
def destroy
#guest = Guest.find(params[:id])
#guest.destroy
redirect_to guests_path
end
private
def guest_params
params.require(:guest).permit(:status, :name, :message, plusones_attributes: [:id, :name, :oldness, :_destroy])
end
end
Models:
class Guest < ActiveRecord::Base
has_many :plusones, dependent: :destroy
belongs_to :user
end
class Plusone < ActiveRecord::Base
belongs_to :guest
end
So ideally I would like to use one Model, and allow additional fields to be entered of the exact same attribute, meaning if I have a Name: field, I should be able to click "Add another name" and add that name, and that would be saved as an individual guest in the table, meaning the first guest would have guest_id of 1, and the dynamically added field for another guest would add a guest with guest_id of 2.
I think you are making a mistake in thinking that there must be a one to one relationship between controllers and models. Have a look at ActiveRecord's Nested Attributes. That shows that you can have a single form submit data to an object and it's sub-objects in one action.
Have a look at fields_for, for information about how to configure a form to allow you to enter data for sub-objects.
When I've worked with nested set, I've found using a JavaScript tool like dynatree is the best way to display the nested data to users. I've also created a gem TreeDecorator, to make it easy to render nested sets as nested HTML lists, that can then be passed to dynatree.
I managed to find a solution that allowed me to keep my nested attributes and still be able to display the related child elements in the index page.
This is what I did to get the plusones related to the guest_id to display within index.html.erb
<% #guest.each do |guest| %>
<% guest.plusones.each do |plus| %>
<%= plus.name %>
<%= plus.oldness %>
<% end %>
<% end %>

Using foreign key in routing

I have some Articles, each with a 'show' page. I am making it so each Article has some comments on the show page as well. I have a 'create' action for Comments, but I need to add a 'redirect' to the 'create' action of the Comments Controller.
I want the redirect to go to the show page of the Article to which the newly created Comment belongs.
How would I write this redirect_to statement?
Here's what I have so far:
def create
#comment = Comment.new(comment_params)
if #comment.save
#article = ?________?
redirect_to #article
end
end
I appreciate your help because I have been befuddled by this concept for ages and really look forward to getting past this hump.
Provided your relationships are set up properly and your routes are defined properly, this is an extremely simple task.
Your models should be (at minimum):
class Article < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :article
end
This ensures the proper helper methods on each model. Now define your routes so comments belong to an article:
// somewhere in routes.rb
resources :articles do
resources :comments
end
This will give you nested routes, most notable the desired create route:
POST /articles/:article_id/comments
Which is where you direct your comment creations, your create will now look like:
// Comments controller
def create
#article = Article.find(params[:article_id]) # Probably should verify this gets something
if #comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
else
render :new
end
end
And that should solve you issue. (If there are any syntax issues or other confusions please let me know - this was pulled all from memory).

How to add current_user to user_id to form_for in rails?

I have a form for creating materials (title, description and content - all basic). The form saves these details just fine but it doesn't save the user_id, which should be the user_id of the current_user. How do I do this? It must be easy but nothing has worked so far.
def create
#material = Material.new(params[:material])
if #material.save
flash[:success] = "Content Successfully Created"
redirect_to #material
else
render 'new'
end
end
def create
#material = Material.new(params[:material])
#material.user_id = current_user.id if current_user
if #material.save
flash[:success] = "Content Successfully Created"
redirect_to #material
else
render 'new'
end
end
There are a few different ways to do it depending on how you have your application setup. If there is a relationship between the user and materials (User has many materials), you could use that in your controller:
def create
#material = current_user.materials.new(params[:material])
# ...
end
If you don't have that relationship, I would still recommend setting it in the controller as opposed to a hidden field in the form. This will be more secure because it won't let someone tamper with the user id value:
def create
#material = Material.new(params[:material].merge(user_id: current_user))
# ...
end
Assuming you are saving the login users's object in the current_user following will work for you
#material = Material.new(params[:material])
#material.user_id = current_user.id
if #material.save
With Rails 5 and parameters needing to be permitted before objects are created, this is the simplest way to merge the current_user into the params, kudos to #Peter Brown in his answer:
def create
#discussion = current_user.materials.new(new_material_params)
# ...
end
private
def new_material_params
params.require(:material).permit(:title, :description,: content)
end
If you have nested object creation using accepts_nested_attributes_for, you need to manually merge deep into the association parameters:
class User < ApplicationRecord
has_many :discussions # Used to associate User with Discussion later
end
class Comment < ApplicationRecord
belongs_to :user
end
class Discussion < ApplicationRecord
belongs_to :user
has_many :comments
accepts_nested_attributes_for :comments
end
class DiscussionsController < ApplicationController
def create
# Merge the params[:discussion][:user_id] by using the relationship's #new
#discussion = current_user.discussion.new(new_discussion_params)
end
private
# Sanitized params for creation, not editing
def new_discussion_params
params.require(:discussion)
.permit(:title, :user_id,
comments_attributes: [:id, :content, :discussion_id, :user_id])
.tap do |discussion_params|
# Require the association parameters, and if they exist,
# set :user_id for each.
discussion_params.require(:comments_attributes).each do |i, comment|
comment.merge!(user_id: current_user.id)
end
end
end
end
Heads up: Setting (or overwriting!) what will be params[:discussion][:comments_attributes]["0"][:user_id] works fine for creation. But if you allow editing deep hierarchies in addition to creation, make sure you don't accidentally overwrite all the :user_ids with the current user.

finding a User object in ruby on rails

Ruby on rails noob here.
User fields relevant to this question: id (prim. key). Post fields relevant to this question: id, user_id (fk). The foreign key is user_id of Posts which is connected to id of Users. Is the following the right syntax? I want to grab the User object who posted the current post:
(this is in controllers/posts_controller.rb)
#id = Post.find(params[:id]).user_id
#user = User.find(#id)
You can see the context of the code below:
def sendMessage
##user = current_user
#id = Post.find(params[:id]).user_id
#user = User.find(#id)
UserMailer.welcome_email(#user).deliver
respond_to do |format|
format.html { render :nothing => true, :status => :ok }
end
end
The reason I'm asking is because an email is not getting sent to the user who created the post. Now, I know that this isn't an email issue because, as a test, I tried commenting out the two lines in question and simply using:
#user = current_user
...and this sends an email to the user who's logged in (but this isn't what I want).
Thanks in advance,
Matt
You should have your models set up as follows:
class Post < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :posts
end
Then in your controller you can do:
#post = Post.find(params[:id])
#user = #post.user
You should set the proper relations in the model.
I guess 'has_many :posts' in User and 'belongs_to :user' in Post.
Then you'll be able to do:
#user = Post.find(params[:id]).user

Resources