How to execute this HABTM console association in the view - ruby-on-rails

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'

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.

Edit author of an article

I have post which belongs_to users, and would like an option when editing to be able to change the user that post is associated with.
I've made a start, but I am very new to Rails and a bit stuck.
I started in my post_controller.rb.
def update
#post.user = associated_user
if #post.update(post_params)
flash[:notice] = "Post was successfully updated"
redirect_to edit_post_path(#post)
else
render 'edit'
end
end
I've defined the method associated_user in my application_controller.rb (I want to do this for a more than just articles.)
def associated_user
#associated_user ||= User.find(session[:user_id]) if session[:user_id]
end
I understand that this code is wrong - I don't want it to get the logged in user, I want to get it from the field I've set in my form view.
<%= f.text_field :associated_user, class: "form-control", placeholder: "Edit Author" %>
Ideally this form field would be a drop down listing all users with a certain status (a boolean I have already set in the users table.)
I'm not sure how far away I am, but if any one is able to offer some guidance, that would be greatly appreciated!
How are the relationships defined in the models. If your post belongs to users, then use a dropdown control in your form:
f.collection_select :user_id, User.whateverscope, :id, :text_method
and when the form is submitted rails will do the rest.
If you have something different set up, then post your models, your strong_params methods, etc.

Nested Forms: Copy fields_for from another object

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

Ruby on Rails: Saving user id manually? Or is there a better method?

Should I manually add a user_id into the hidden form? Or is there a better way?
I have models:
class Project < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :projects
end
In my view:
<%= simple_form_for #project do |f| %>
<%= f.hidden_field :value => current_user.id %>
<% end %>
Or is there another way to do this? I thought if the model was associated with each other, it would automatically add the user_id into the database for projects?
Thanks!
Edit: Converted all trip to projects, so others needing help knows
Nope. That shouldn't be in form, because of everybody can change hidden value at the form.
Assuming you are using strong parameters
def create
#project = Project.new(project_params)
#project.save # or something else
end
def project_params
params.require(:project).permit(something_permitted_here)
.merge(user_id: current_user.id)
end
If you put user id on hidden form it will cause a breach in security. Because using browser tools a hacker can change the user_id and can inject information for other user. A better way is to put it into controller.
Inside controller, you can do so:
def create
#project.user = current_user
#project.save
#...
end
This way you protect yourself against someone manually changing the user_id in html.
Use the association to your advantage:
def create
#project = current_user.projects.build(project_params)
if #project.save
# ...etc
end

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).

Resources