2009-07-06 27 views
5

Tengo dos modelos generados por andamios, estudiante y clase. Tienen una relación de muchos a muchos implementada con has_and_belongs_to_many.
me gustaría ser capaz de cambiar la cual clase it un estudiante es en, así como los que estudiantes s están tomando cada clase . Es decir, quiero modificar una estudianteclase variable (agregar y eliminar elementos de ella) y viceversa.
¿Cómo hago esto RESTfully?
Si estoy eliminando una clasede un estudiante 's clases la lista, entonces parece como si quisiera llamar actualización en mi students_controller. Si este es el caso, ¿qué debería pasar como parámetro para modificar la variable clases? Otra colección de clase es (con la adecuada clase eliminada)?
Mi otro pensamiento es simplemente llamar a alguna acción en el control de estudiantes (por ejemplo, eliminar_clase) y pasar el ID de la clase para eliminar. Esto parece sensitivo pero no RESTful.
¿Cómo actualizo RESTfully una colección de has_and_belongs_to_many?

¿Cuál es la mejor manera de hacerlo?

Respuesta

8

La clave para resolver esto es identificar correctamente el recurso que está modificando. En este caso, el recurso que está modificando es la relación entre la clase y el alumno, a la que me referiré como Enrollment.

Se ha vuelto habitual en Rails utilizar has_many :through preferentemente en has_and_belongs_to_many. Es posible que desee cambiar la lógica de su dominio para que se ajuste a la costumbre, pero también puede cambiar la tendencia, si está realmente seguro de que no es necesario almacenar metadatos sobre la relación.

Una de las ideas clave para REST es que los recursos RESTful no necesitan mapear a los modelos. Debe crear un EnrollmentsController y añadir una línea a config/routes.rb:

map.resources :enrollments 

continuación, puede crear y borrar sus relaciones, así:

class EnrollmentsController < ApplicationController 
    def create 
     @student = Student.find(params[:student_id]) 
     @course = Course.find(params[:course_id]) 
     @student.courses << @course 
     if @student.save 
     #do happy path stuff 
     else 
     #show errors 
     end 
    end 

    def destroy 
     @student = Student.find(params[:student_id]) 
     @course = @student.courses.find(params[:course_id]) 
     @student.courses.delete(@course) 
    end 
end 

Y se puede hacer botones para esas acciones como por lo que:

<%= button_to "Enroll", enrollments_path(:student_id => current_student.id, :course_id => @course.id), :method => :post %> 
<%= button_to "Withdraw", enrollment_path(1, :student_id => current_student.id, :course_id => @course.id), :method => :delete %> 

el 1 en la línea superior actúa como un marcador de posición donde el :enrollment_id debería ir y es un poco de vineg sintáctica Para recordarle que se está resistiendo a la voluntad del marco de Rails.

+0

Gracias, funcionó muy bien con dos cambios menores: 1. calling @ student.courses.destroy (params [: course_id]) me dio un ActiveRecord :: AssociationTypeMismatch ya que estaba buscando un curso y obtuve un String. Lo solucioné al encontrar el curso correcto (@ student.courses.find (params [: course_id])) y luego eliminarlo. 2. No quiero destruir el curso, simplemente elimine la asociación, así que utilicé una llamada de eliminación en lugar de destruir –

+0

Me alegro de que funcionó. He actualizado mi ejemplo para reflejar sus cambios. – austinfromboston

0

Si esto es realmente REST, entonces simplemente cambiaría la clase y el alumno al mismo tiempo, agregando la relación a ambos o eliminándolos de ambos. Después de comprobar que el modelo cargado era autoconsistente, lo compararía con el anterior y haría los cambios necesarios. Si este cambio se basa en las clases o en los estudiantes se convierte en un detalle de implementación.

Otros dos opciones:

usted podría hacer la información de estado REPOSO menos redundantes, dejando que sea la clase o el estudiante son dueños de la relación, pero que no se ajusten a sus necesidades.

También podría mantener la información de la relación en ambos lados, pero decidir que uno es canónico. Entonces, por ejemplo, si tener un alumno es propiedad de la clase, no del estudiante, entonces se ignora cualquier cambio a los pertenecientes al alumno.

Prefiero la primera opción, pero creo que al menos deberían considerarse.

0

No estoy seguro de que quiera hacer ambas cosas con REST. Probablemente consigas más kilometraje creando una relación de eliminación en cascada entre ambos.

Aquí hay un debate sobre SO en Cascade Deleting on Rails.

0

¿podría tener dos recursos anidados?

dependiendo de si tiene params[:class_id] (¡no diga que llamó a su clase modelo!) O si usted tenía un params[:student_id] Se podría decir que es el objeto en cuanto a que la operación está llamado (ver ejemplo la creación unida en un lado):

class SchoolClassController < ApplicationController 

before_filter :get_school_class, :except => [:new, :create, :index] 
before_filter :get_student 

def create 
    get_school_class(false) 
    get_student(false) 
    if @school_class 
    if @student 
     (@school_class.students << @student) 
    else 
     redirect_to "/" and return 
    end 
    else 
    @school_class = SchoolClass.new(params[:school_class]) 
    end 
    if @school_class.save 
    redirect_loc = @student ? student_school_class_path(@student, @school_class) : school_class_path(@student) 
    redirect_to redirect_loc and return 
    else 
    redirect_to "/" 
    end 

end 

private 

def get_school_class(will_redirect=true) 
    begin 
    @school_class = SchoolClass.find(params[:id]) 
    rescue AR::RNF 
    (redirect_to "/" and return false) if will_redirect 
    end 
end 

def get_student(will_redirect=true) 
    if params[:student_id] 
    begin 
     @student = Student.find(params[:student_id]) 
    rescue AR:RNF 
     (redirect_to "/" and return false) if will_redirect 
    end 
    end 
end 

end 

excusa los errores! En realidad debería usarse como un ejemplo teórico

Cuestiones relacionadas