Nested Forms: Copy fields_for from another object - ruby-on-rails

Implemented nested_form for a has_may relationship between Person and Post models.
class Person
has_many :posts
accepts_nested_attributes_for :posts
end
It works well. Now there is button "Copy Person" that redirects to new person page by per populating all attributes of previous person. All the fields of person is populated except the posts of that person. Here is the view code of rendering posts:
<%= f.fields_for :posts do |ff| %>
<%= render 'post_fields', ff: ff %>
<% end %>
controller:
def copy_person
#person = Person.new
#previous_person = Person.find(params[:id])
redirect_to new_person_url(#person)
end
If a person has 3 posts and it should populate all fields of person and should display 3 posts as well while copying it. The previous person object is available on view as #previous_person
What change I need in view to render those posts?

Please consider to create new person and theirs posts according to previous_person's attributes
def copy_person
previous_person = Person.find(params[:id])
#person = Person.create!(previous_person.attributes.except("id"))
previous_person.posts.each do |old_posts|
#person.posts.create!(old_posts.attributes.except("id"))
end
end
Now you have newly created person copied from the previous one with all posts. Also, you can use dup method to create a copy of person
def copy_person
previous_person = Person.find(params[:id])
#person = previous_person.dup
#person.save!
previous_person.posts.each do |old_posts|
#person.posts.create!(old_posts.attributes.except("id"))
end
end

Related

How to update existing model when another is created?

I have two models, User, and Product. Product belongs to User, User has many Products.
When a Product is created I also want to update multiple fields in the User model. I've been developing with Ruby for like 2 years now and still don't understand forms fully when it comes to this stuff. I'm still getting permitted: false. Now I know that for instance if I was creating a user while also creating a product I would just do #product.user.build but in this case I just want to update an already existing record.
I also realize that I probably can't call f.fields_for :user as #product doesn't know about user yet. In my head I believe I should be able to just pass additional params to the form, grab the current_user in the product#create action and then update the attributes manually by calling update_attributes on user.
product.rb
accepts_nested_attributes_for :user
product controller
def new
#product = Product.new
end
params.require(:product).permit(:product_name, user_attributes: [:phone_number, :email_address])
product view
form_for #product do |f|
f.fields_for :user do |c|
c.text_field :phone_number
c.text_field :email_address
f.text_field :product_name
end
I also realize that I probably can't call f.fields_for :user as #product doesn't know about user yet.
You can assign attributes to #product without saving it.
def new
#product = Product.new(
user: current_user
)
end
Now #product.user works.

How could I add to a table from from a different model HTML page?

I'm new to rails. I have a table TeamMemberships that has a foreign key to Students and a foreign key to Teams. I want to be able to have a link in the show.html.erb page for a specific team that redirects to a form that lets me add a row to TeamMemberships in which the Team foreign key is the team whose show.html.erb page the link was clicked on and the Student foreign key would be determined by the form. Is this possible?
Thanks!
Add a new resources in your routes.rb file like so:
Rails.application.routes.draw do
resources :teams do
resources :team_memberships
end
end
Which will create the CRUD routes:
team_team_memberships GET /teams/:team_id/team_memberships(.:format) team_memberships#index
POST /teams/:team_id/team_memberships(.:format) team_memberships#create
new_team_team_membership GET /teams/:team_id/team_memberships/new(.:format) team_memberships#new
edit_team_team_membership GET /teams/:team_id/team_memberships/:id/edit(.:format) team_memberships#edit
team_team_membership GET /teams/:team_id/team_memberships/:id(.:format) team_memberships#show
PATCH /teams/:team_id/team_memberships/:id(.:format) team_memberships#update
PUT /teams/:team_id/team_memberships/:id(.:format) team_memberships#update
DELETE /teams/:team_id/team_memberships/:id(.:format) team_memberships#destroy
What is interesting you here is the new_team_team_membership GET /teams/:team_id/team_memberships/new(.:format) that you can call using the named route new_team_team_membership.
You will be able to pass the team ID for which you'd like to create the TeamMembership in the route with new_team_team_membership_path(#team.id).\
Regarding the student_id you will pass it in the HTTP POST request body.
Here is how to create the link from the team show page to the TeamMembership form:
<%= link_to 'Add a student', new_team_team_membership_path(#team.id) %>
You will have to create the TeamMembershipsController and the new action. The action should initialise the #new_team_membership variable that will be used by the form and load the student list like so:
class TeamMembershipsController < ApplicationController
def new
#new_team_membership = TeamMembership.new(team_id: params[:team_id])
#students = Student.all
end
end
Then you have to create the app/views/team_memberships/ folder and add the new.html.erb file with the following form:
<%= form_for #new_team_membership do |f| %>
<%= f.select :student_id, options_for_select(#students.collect{ |student| [student.name, student.id]) %>
<%= f.submit %>
<% end %>
Then in your TeamMembershipsController controller, you have to define the create action that will receive in parameter the team_id, and from the form the selected student_id. You will then be able to create the TeamMembership instance.
Update
Regarding the create action of the TeamMembershipsController controller, here is how it should be done:
class TeamMembershipsController < ApplicationController
def new
# ...
end
def create
# The form should have embedded all the params within a key with the model's name
# like `:team_membership`.
student = Student.find(params[:team_membership][:student_id])
#team_membership = TeamMembership.new(
team_id: params[:team_id],
student: student
)
if #team_membership.save
redirect_to :team_path(params[:team_id])
else
# Reload again the student as we will show errors.
# The student dropdown would be empty.
#students = Student.all
# Render again the new form
render :new
end
end
end

How to execute this HABTM console association in the view

I have a User model, and two models that inherit from that: Teacher and Student. They also have their own controllers that inherit from the User controller.
I also have a Group model.
group belongs to teacher, teacher has many groups. group has and belongs to many students. student has and belongs to many groups.
There is a join table for the HABTM relationships, called 'groups_students'.
I have managed to create a form element that allows me to set the 'type' of User to 'Student' or 'Teacher'.
The difficulty I have now is assigning students to groups in the view. I can do this in the console no problem, as outlined here: Rails 4 HABTM how to set multiple ids in console?
I'm stumped as to how to do this in a view. I want to do it in the Student's show view.
Can someone at least give me some guidance? I have a pretty good book on Rails, but I need to know roughly what I have to do.
In the end I actually paid someone to solve this for me, because I couldn't find any free help for this on the internet. Thankfully, it wasn't too easy for him to solve, but solve it he did:
views/users/show.html.erb:
<%= form_tag assign_to_group_path do %>
<%= hidden_field_tag :user_id, #user.id %>
<%= select_tag :group_id, options_from_collection_for_select(Group.all, "id", "title") %>
<%= submit_tag "Assign to Class" %>
<% end %>
users_controller.rb:
private
def user_params
params[:user].permit(:type) if params[:user]
end
students_controller.rb (inherits from users_controller.rb):
def assign_to_group
#user = User.find(params[:user_id])
#group = Group.find(params[:group_id])
#user.groups << #group unless #user.groups.include? #group
#user.save!
redirect_to user_path #user
end
routes.rb:
post 'assign_to_group' => 'students#assign_to_group'

Committing Into the Database Trying to Change the Form a Different Way

I have a classified model that is hook up to the student model in a way that it looks like this
classified.rb
belongs_to :student
in my student model it
has_many :classifieds
The classifieds table contain a column for student_id
when I head to the rails console
I can create a new classified for that particular student record by doing
Student.find(19).classifieds.create(:ad_title => "blah", :ad_content => "blah", :location => "blah")
The record automatically gets a student_id generated because it was created in the student standpoint.
Now the problem is I have a classifieds controller with a new and a create method and a new form in the view
I am creating a new ad in the classifieds standpoint here is the form
<div>
新广告: <br><br>
<%= simple_form_for #advertisement do |f| %>
<%= f.input :ad_title, label: "Title" %><br>
<%= f.input :ad_content, label: "Content"%><br>
<%= f.input :location, label: "Location"%><br>
<%= f.input :student_id, label: "Your Student ID"%><br>
<%= f.button :submit, "Add an Advertisement" %>
<% end %>
</div>
The problem is the student_id must exist in order for that particular record to show up in index and my users don't know their id.
How do I create a classified in the student standpoint using forms
I consider going to my students controller and adding a method like this
def create_classified_ad
#student = Student.find(params[:id])
#classified = #student.classified.create(params[:classified])
end
I am using devise so there is user session? I don't know how sessions work entirely I want that particular student to be found after logged in and have that student create a classified ad with the inputs from the form so the record will be created correctly?
The question is more like how do you insert data using forms to a hooked up table correctly? (and not merely inserting data into a simple no relationship table)
You should not be passing the student_id from the form. If you have a devise session, you should have access to current_user:
def new
#classified = current_user.classifieds.build
end
def create
#classified = current_user.classifieds.build(params[:classified])
if #classified.save
# do something
else
# handle failure
end
end
In the index, you can do this:
def index
#classifieds = current_user.classifieds.all
end
All of this assumes you have a devise session. If not, just fetch the student like so:
#student = Student.find(params[:student_id])
#classified = #student.classifieds.build
Set up a nested route for the classifieds under the user:
resources :users do
resources :classifieds
end
This allows you to fetch the student from a student_id param. Your route would like this: new_student_classified_path(#student).

rails create page from

I have Categories and Products. A product has a relation
belongs_to :category
In the categories show page I have a button to add a new product. This button goes to a page where I create the new product, but I need to give the category to the new product.
How can I pass the id from the category page where I was to the new Product? So, if I am in the category Electronic I click 'Add product' and this product automaticaly is associated with Eletronic category.
Hope you can understand what I want.
Thanks
You need to pass the category_id in your link, e.g. new_product_path(category_id: #category.id).
You will also need to have a field in your product form to save the category's ID, e.g <%= f.hidden_field :category_id, params[:category_id] %>
First, I would decide whether each product is contained within a category, or whether it's simply associated with a category. Hints it is contained would be:
You expect each product to have exactly one 'parent' category.
You expect each product will always appear in the context of its parent category.
If and only if you believe this to be the case, I would be tempted to nest the product resource within the category.
# routes.rb
resources :categories do
resources :products
end
# products_controller.rb (SIMPLIFIED!)
class ProductController < ApplicationController
before_filter :get_category
def new
#product = #category.products.build
end
def create
#product = #category.products.build(params[:product])
if #product.save
redirect_to #product
else
render template: "new"
end
end
def get_category
#category = Category.find(params[:category_id])
end
end
If you do this, rails will ensure your product is associated with the right category. The magic happens in #category.products.build, which automatically sets the category_id based on the relationship.
If you'd rather keep categories and products as simple associations, I'd just use a query parameter as per Eric Andres answer, although I'd be tempted to handle it in a slightly different way:
# link:
new_product_path(category_id: #category.id) # So far, so similar.
# products_controller.rb
class ProductsController < ApplicationController
def new
#product = Product.new
#product.category_id = params[:category_id].to_i if params[:category_id]
end
end
# new.erb
<%= f.hidden_field :category_id %>
This is mostly just a stylistic difference. Eric's answer will work too - I just prefer to set the value on the model itself rather than have the view worry about parameters etc.

Resources