0

I have an association of One Classroom has Many Students. I want to create a form where I can create a student and assign him a classroom. And I am having problems creating the form.

model/classroom.rb

class Classroom < ActiveRecord::Base
  has_many :students
end

model/student.rb

class Student < ActiveRecord::Base
  belongs_to :classroom
end

I want to create a new student and assign it to a certain classroom.

<%= form_for(@student) do |f|%>


<%= f.label :name %>
<%= f.text_field :name %>

<br />
<br />

<%= f.label :student.classroom.number %>       #Is this correct? 
<%= f.text_field :student.classroom.number %>  #Is this correct?

<%= f.submit %>

<%end%>

The attributes for each model are

1.9.3-p448 :026 > Classroom
 => Classroom(id: integer, number: string, created_at: datetime, updated_at: datetime) 
1.9.3-p448 :027 > Student
 => Student(id: integer, name: string, created_at: datetime, updated_at: datetime, classroom_id: string) 

students_controller

class StudentsController < ApplicationController
  def index
    @students = Student.all
  end

  def show
    @student = Student.find(params[:id])
  end

  def new
    @student = Student.new
  end

  def create
    @student = Student.new(article_params)
    respond_to do |format|
    if @student.save
      format.html {redirect_to(@student, notice: 'Student was successfully created.')}
    else
      format.html {render action: "new"}
    end
end
  end

  private
    def article_params
      params.require(:student).permit(:name, :classroom_id)
    end
  end

classroom_controller

class ClassroomsController < ApplicationController
  def index
    @classrooms = Classroom.all
  end

  def show
    @classroom = Classroom.find(params[:id])
  end

  def new
    @classroom = Classroom.new
  end

  def create
    @classroom = Classroom.new(article_params)
    respond_to do |format|
    if @classroom.save
      format.html {redirect_to(@classroom, notice: 'Classroom was successfully created.')}
    else
      format.html {render action: "new"}
    end  
end
  end

  private
    def article_params
      params.require(:classroom).permit(:number)
    end
  end
RailsNewbie
  • 155
  • 11
  • Are you wanting to assign the Student to an existing Classroom or a new Classroom? – bdx Aug 30 '14 at 14:34
  • existing classroom. :) – RailsNewbie Aug 30 '14 at 14:39
  • @RailsNewbie no it isn't correct. Use hidden field to send your classroom number or use a select from where a user can select from a list of classrooms or you can assign class in the controller method itself. Decide what you wana do depending on your apps design – Mandeep Aug 30 '14 at 14:42

2 Answers2

1

Assuming you want to assign the Student to an existing Classroom:

First, ensure the Student model has the following in the attr_accessible:

attr_accessible :classroom_id

In your form, instead of your second label/text_field, you should then be able to do:

<%= f.label :classroom %>
<%= f.select(:classroom_id, Classroom.all.pluck(:number)) %>

Note that for the f.select method you must pass the attribute you are setting, not the association name (i.e., classroom_id not classroom)

Also note that best practice would be to move logic associated with collecting information from a model (i.e. Classroom.all.pluck(:number)) into an instance variable in the controller,

e.g. @classrooms = Classroom.all.pluck(:number)

and using that @classrooms instance variable in your view instead.

Aside from the above, you should also read some more about symbols. What you've tried there with :student.classroom.number isn't going to work how you thought it might. There's a good SO question about it here: When to use symbols instead of strings in Ruby?

Community
  • 1
  • 1
bdx
  • 3,316
  • 4
  • 32
  • 65
  • Using Rails Console: .... Using your method: ---- the "classroom_id" got persisted as "1E1" instead of "1" which is actual classroom_id – RailsNewbie Aug 30 '14 at 14:57
  • And "attr_accessible" created a NoMethod error. Instead, I used "params.require(:student).permit(:name, :classroom_id)" – RailsNewbie Aug 30 '14 at 14:59
  • When I changed to "<%= f.select(:classroom_id, Classroom.all.pluck(:id)) %>", the Student will get persisted as classroom_id: "1". I am confused, please help :( – RailsNewbie Aug 30 '14 at 15:04
  • 1
    Re the attr_accessible error, I assumed you were using Rails 3, as your console output showed you on Ruby 1.9.3. My mistake. Glad you got another solution above. – bdx Aug 30 '14 at 23:32
  • At least now I know attr_accessible is irrelevant in rails4. thanks bdx :) – RailsNewbie Aug 31 '14 at 04:16
1

You can set a hidden field setting it to the classroom itself, if you already know which classroom you want to add him in:

<%= f.hidden_field, :classroom_id, value: here_you_put_the_classroom_id $>

And don't forget to add :classroom_id in the permitted params in your controller.

Another way you can do if you want the option to select the classroom you are putting the student in, you can create a select field passing all the classrooms.

<% classroom_array = Classroom.all.map { |classroom| [classroom.name, classroom.id] } %>
<%= options_for_select(classroom_array) %>

Don't forget to add the permitted params again. Hope it helps.

***UPDATE***

The options_for_select should go inside de select tag, like this:

<% classroom_array = Classroom.all.map { |classroom| [classroom.number, classroom.id] } %> 
<%= f.label :classroom %> 
<%= f.select(:classroom_id, options_for_select(classroom_array)) %>

***UPDATE 2***

Pluck could also be an option, as long as you pass the classroom id as param. So, the code can be refactored to:

<%= f.label :classroom %>
<%= f.select :classroom_id, Classroom.all.pluck(:name, :id) %>
Jesse Mignac
  • 305
  • 2
  • 10
  • Changed to <% classroom_array = Classroom.all.map { |classroom| [classroom.number, classroom.id] } %> <%= options_for_select(classroom_array) %> but I dont see any select dropdown. – RailsNewbie Aug 30 '14 at 15:31
  • Its works. This is the working code: <% classroom_array = Classroom.all.map { |classroom| [classroom.number, classroom.id] } %> <%= f.label :classroom %> <%= f.select(:classroom_id, options_for_select(classroom_array)) %> – RailsNewbie Aug 30 '14 at 15:35
  • The code I had was about cities, so I missed that. sorry. – Jesse Mignac Aug 30 '14 at 15:37
  • Jesse, if you are free, could you explain to me the code? Why yours worked but not bdx's? don't quite get it. Also, how can i refactor the code as such as I could make use of the students_controller instead? – RailsNewbie Aug 30 '14 at 15:47
  • Oh, sure. I started a couple of months ago, but I can give it a try. Actualy, bdx assumed you were using a previous version of rails, that's why the attr_accessible didn't work for you. We don't need to used that in rails 4 anymore, but the strong params, as you did. In rails for, in the other hand, we still have the attr_accessor method, to be used when we want to manipulate a modal attribute that is not to be saved in the database. For exemple: terms of use, that must accepted before user creation... – Jesse Mignac Aug 30 '14 at 16:02
  • Pluck, actualy, should do the same thing as the map method did. So, I believe that if you change the <%= f.select(:classroom_id, options_for_select(classroom_array)) %> for <%= f.select(:classroom_id, options_for_select(Classroom.all.pluck(:number, :id)) %> you will have the same result, and you could also throw alway the line that sets classroom_array – Jesse Mignac Aug 30 '14 at 16:04
  • 1
    In fact, pluck and map returns an array of arrays. If each array has only one option, the selected option has as value the same thing it is shown in the options, so <%= f.select(:classroom_id, Classroom.all.pluck(:number)) %> tries to set a student in a classroom that has the id classroom.number, and not classroom.id. To fix that, you need to pass the array [name, id]. Name is what will be shown, and id will be the value if selected. Like this, you set the student to the classroom that has the id classroom.id – Jesse Mignac Aug 30 '14 at 16:12
  • That way, you can even try to refactor the code to <%= f.select(:classroom_id, Classroom.all.pluck(:number, :id)) %>. This way, the classroom.number is what is shown in the select options, and the classroom.id is its value if selected. But I am not sure if this structure will work, maybe it needs the be clear it is an array with "Options_for_select". Can you try this and tell if it worked? Was it clear? I get confusing sometimes when trying to explain lol I think that's all I know so far. – Jesse Mignac Aug 30 '14 at 16:16
  • Yup, <%= f.select(:classroom_id, Classroom.all.pluck(:number, :id)) %> works too! Thanks Jesse :) – RailsNewbie Aug 31 '14 at 04:15