2009-07-06 4 views
5

У меня есть два эскимосных модели, студент и класс. У них есть много-много взаимосвязей, реализованных с has_and_belongs_to_many.
Я хотел бы быть в состоянии изменить который класс эс в студент находится в, а также какие студенческие s принимают каждый класс. То есть, я хочу изменить ученикклассы переменная (добавление и удаление элементов из нее) и наоборот.
Как мне это сделать?
Если я удаление класса из студента «s классов списка, то кажется, что я хочу назвать обновления на моем students_controller. Если это так, то что мне следует передать в качестве параметра для изменения переменной ? Другая коллекция класс es (с надлежащим класс удален)?
Моей другой мыслью является просто вызвать какое-то действие в student_controller (скажем, remove_class) и передать идентификатор класса для удаления. Это кажется чувственным, но не RESTful.Как обновить коллекцию has_and_belongs_to_many RESTfully?

Каков наилучший способ для этого?

ответ

8

Ключом к его решению является правильная идентификация ресурса, который вы изменяете. В этом случае ресурс, который вы изменяете, - это отношение между классом и учеником, которое я буду называть Enrollment.

В Rails принято использовать has_many :through, предпочтительно has_and_belongs_to_many. Возможно, вы захотите изменить свою логику домена, чтобы она соответствовала обычаю, но вы также можете выиграть тренд, если вы действительно уверены, что метаданные не должны храниться в отношении отношений.

Одна из ключевых идей для REST заключается в том, что ресурсам RESTful не нужно сопоставлять модели. Вы должны создать EnrollmentsController и добавить строку в конфиг/routes.rb:

map.resources :enrollments 

Затем вы можете создавать и удалять свои отношения как так:

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 

И вы можете сделать кнопки для таких действий, как так:

<%= 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 %> 

1 на линии выше, действует как заполнитель, где :enrollment_id должен пойти и является капельку синтаксической vineg ar, чтобы напомнить вам, что вы раскалываете волю рамки Rails.

+0

Спасибо, что отлично поработали с двумя незначительными изменениями: 1. вызов @ student.courses.destroy (params [: course_id]) дал мне ActiveRecord :: AssociationTypeMismatch, так как он искал курс и получил строку. Я исправил это, сначала найдя правильный курс (@ student.courses.find (params [: course_id])), а затем удалив это. 2. Я не хочу уничтожать курс, просто удалите ассоциацию, поэтому я использовал вызов для удаления вместо уничтожения. –

+0

Рад, что сработал. Я обновил свой пример, чтобы отразить ваши изменения. – austinfromboston

0

Если это действительно ОТДЫХ, то вы просто измените класс и ученика одновременно, либо добавив отношение к обоим, либо удалив их из обоих. После проверки того, что загруженная модель была самосогласованной, вы затем сравнивали ее с предыдущей и делали любые изменения. Является ли это изменение управляемым классом или управляемым студентом, становится детальностью реализации.

еще два варианта:

Вы могли бы сделать информацию о состоянии покоя менее многословна, позволяя либо класс или студент владеют отношениями, но которые могут не соответствовать вашим потребностям.

Вы также можете сохранить информацию о взаимоотношениях с обеих сторон, но решите, что она является канонической. Так, например, если наличие студента принадлежит классу, а не ученику, то любое изменение на member_to ученика игнорируется.

Я предпочитаю первый вариант, но я считаю, что это должно быть, по крайней мере, рассмотрено.

0

Я не уверен, что вы хотите сделать их оборонно. Вероятно, вы получите больше пробега, создав связь каскадного удаления между ними.

Вот так обсуждается на Cascade Deleting on Rails.

0

у вас может быть два вложенных ресурса?

зависит от того, есть ли у вас params[:class_id] (пожалуйста, не говорите, что вы назвали свой модельный класс!) Или были ли вы params[:student_id] вы могли бы сказать, что объект, относительно которого операция вызывается на (прилагается образец создания на одной стороне):

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 

извинить ошибки! Следует действительно использовать в качестве теоретического примера