0

간단한 설문 조사/설문지 앱을 만들려고합니다. 설문 조사는 Questions입니다. 대부분의 질문은 설문지 작성자가 자유 텍스트 응답으로 작성하는 단일 내용 필드 (질문 자체)로 구성됩니다. (이 토론과 관련없는 몇 가지 다른 필드도 있습니다.) 그러나 사용자는 MultipleChoiceQuestions 또는 LikertQuestions (예 : 1 ~ 5 척도의 답변)을 만들 수도 있습니다. MultipleChoiceQuestions의 경우 MultipleChoiceQuestion has_many Answers과 같은 Answer이라는 다른 모델이 있습니다. 여기 내 디자인 선택은 내가 아는 지금까지로, 다음과 같습니다예상대로 STI/ActiveRecord 모델을 얻을 수 없습니다.

class Question < ActiveRecord::Base 
    attr_accessible :id, :content 
end 

class MultipleChoiceQuestion < Question 
    attr_accessible :type 
end 

class LikertQuestion < Question 
    attr_accessible :type, :min, :max, :label_min, label_max 
end 

2) 공유 속성과 메소드를 모듈/믹스 인을 사용 :

module Question 
    @content, @id 
    def method1 
    end 
end 

class MultipleChoiceQuestion < ActiveRecord::Base 
    include Question 
end 

class LikertQuestion < ActiveRecord::Base 
    include Question 
    attr_accessible :type, :min, :max, :label_min, label_max 
end 

1) 질문에서 상속 이것은 상속의 명백한 사례 인 것처럼 보이므로 옵션 1로갔습니다. 그 이후로는 작동하지 않습니다. 단일 테이블 상속은 충분히 단순 해 보였으므로 MultipleChoiceQuestionLikertQuestion을 스키마에 각각 type:string 부여했습니다. 여기에 (dB/schema.rb에서) 각각에 대한 스키마입니다

create_table "questions", :force => true do |t| 
    t.integer "parent" 
    t.string "type" 
    t.string "content" 
    t.datetime "created_at", :null => false 
    t.datetime "updated_at", :null => false 
    t.integer "survey_id" 
    end 

create_table "multiple_choice_questions", :force => true do |t| 
    t.datetime "created_at", :null => false 
    t.datetime "updated_at", :null => false 
    t.string "type" 
    end 

    create_table "likert_questions", :force => true do |t| 
    t.integer "min" 
    t.integer "max" 
    t.string "label_min" 
    t.string "label_max" 
    t.datetime "created_at", :null => false 
    t.datetime "updated_at", :null => false 
    t.string "type" 
    end 

내가 옵션 1을 구현하는 경우 schema.rb에 명시된 바와 같이, 위, 다음 MultipleChoiceQuestion 및 LikertQuestion 어떻게 든 실제로 고유의 필드 중 하나를 포함하지 않는 ; 대신, 그들은 Question의 상속 된 필드만을가집니다. 콘솔 출력을 참조하십시오에 StackOverflow에

1.9.3p392 :001 > Question 
=> Question(id: integer, parent: integer, content: string, created_at: datetime, updated_at: datetime, survey_id: integer) 
1.9.3p392 :002 > LikertQuestion 
=> LikertQuestion(id: integer, parent: integer, content: string, created_at: datetime, updated_at: datetime, survey_id: integer) 
1.9.3p392 :003 > MultipleChoiceQuestion 
=> MultipleChoiceQuestion(id: integer, parent: integer, content: string, created_at: datetime, updated_at: datetime, survey_id: integer) 
1.9.3p392 :004 > LikertQuestion.new(:min => 3) 
ActiveRecord::UnknownAttributeError: unknown attribute: min 

누군가가 질문 추상 클래스해야한다고 말했다. 내가 Question.rb에 self.abstract_class = true를 추가한다면, 나는 다음과 같은 얻을 :

1.9.3p392 :001 > Question 
=> Question(abstract) 
1.9.3p392 :002 > LikertQuestion 
=> LikertQuestion(id: integer, min: integer, max: integer, label_min: string, label_mid: string, label_max: string, created_at: datetime, updated_at: datetime, type: string) 
1.9.3p392 :003 > MultipleChoiceQuestion 
=> MultipleChoiceQuestion(id: integer, created_at: datetime, updated_at: datetime, type: string) 
1.9.3p392 :004 > LikertQuestion.new(:content => "foo") 
ActiveRecord::UnknownAttributeError: unknown attribute: content 

LikertQuestionMultipleChoiceQuestion 고유의 필드를 표시하고 부모로부터 필드를 상속하지 않습니다.

1) 여기에 무엇이 누락 되었습니까? 어쨌든 상속이 최적의 솔루션인지 여부에 관계없이 확실한 것을 간과해야합니다.

2) 상속 대신 모듈 접근 방식을 사용해야합니까? 앞서 언급했듯이, 상속은 아무렇지도 않게 느껴졌습니다. LikertQuestionMultipleChoiceQuestion은 실제로 Questions의 종류입니다. 모듈 접근법을 사용하면 survey.questions(), survey.questions.build() 및 아마도 다른 유용한 것들을 말할 수있는 능력을 잃게됩니다. 이 상황에서 Rails hotshots는 무엇을합니까? 그게 뭐든 할거야.

StackOverflow에 대한 글은 아니지만, 서브 클래 싱과 mixin의 장단점에 대한 매우 포괄적 인 토론을 제공합니다.

Ruby 1.9.3 (2.0으로 전환 할 생각 임), Rails 3.2.3.

+0

레일 1.9.3 또는 루비 1.9.3? – sosborn

+0

@sosborn : "Rails 1.9.3"은 오타 일 뿐이며 * Ruby * 1.9.3을 사용하고 있다는 것이 꽤 분명하다고 생각합니다. –

+0

@mu, 실수라고 알고 있지만 오타라고 생각하지 않습니다. – sosborn

답변

3

당신은 분명 뭔가 분명하지 않습니다. STI가 무엇을 의미하는지 알고 있습니까? 단일 테이블 상속. 여러 테이블을 작성한 후 STI를 사용하려고합니다.

표가 동일하거나 매우 유사한 경우 (어쩌면 1 필드 차이가 나는 경우) STI를 사용해야합니다. 주로 하위 클래스로 분류 한 다음 동작을 구분하는 메서드를 제공하려는 경우 주로 사용됩니다.예를 들어 모든 사용자가 동일한 속성을 공유하지만 일부는 관리자입니다. (이것은 그 자체에 STI를 보증하지 것이다 아마 분명히 아주 간단한 예입니다)

class Admin < User 
    def admin? 
    true 
    end 
end 

class NormalUser < User 
    def admin? 
    false 
    end 
end 

: 당신은 사용자 테이블의 type 필드를 가질 수 있습니다, 그리고 당신은 이런 일이있을 수 있습니다.

추상 클래스가 실행되는 한, 수퍼 클래스에서 모든 동작을 상속해야하는 여러 테이블이있는 경우 좋은 결정입니다. 그것은 귀하의 경우에 이해가 될 것 같습니다; 그러나 추상 클래스에는 테이블이 없다는 점에 유의해야합니다. abstract_class을 선언하는 요점은 존재하지 않는 테이블을 찾을 때 ActiveRecord가 혼란스럽지 않게하기 위해서입니다. 이를 사용하지 않으면 ActiveRecord는 STI를 사용 중이며 질문 테이블을 찾으려고합니다. 귀하의 경우, 당신은 질문 테이블을 가지고, 그래서 그것을 추상적 인 클래스로 선언 정말 말이되지 않습니다.

또 다른 한 가지, "내가 상속 대신 모듈 접근 방식을 사용해야합니까?" 모듈을 사용하는 것은 실제로 Ruby에서 상속의 한 형태입니다. 모듈을 포함 시키면 상위 클래스와 마찬가지로 조상 체인에 삽입됩니다 (모듈은 수퍼 클래스 이전에 삽입됩니다). 나는 어떤 형태의 상속이 올바른 접근이라고 생각한다. 이 경우 두 가지 유형의 질문이기 때문에 추상 질문 수퍼 클래스를 만드는 것이 나에게 의미가 있습니다. 질문은 많은 특성을 공유하지 않으므로 별도의 테이블에 저장하는 것이 내 의견으로는 최상의 솔루션입니다. STI는 데이터베이스에 null을 많이 생성하기 때문에 필드가 여러 개인 경우 실제로는 좋지 않습니다.

모듈에 대해 분명히 밝히고 싶습니다. 다른 관련이없는 여러 모델이 공통된 동작을 공유 할 때 가장 좋다고 생각합니다. 여러 번 사용한 예는 Commentable 모듈의 아이디어입니다 (ActiveSupport :: Concern 사용). 모델이 관련되어 있지 않기 때문에 여러 모델이 주석 처리 될 수 있기 때문에 수퍼 클래스를 보증 할 필요는 없다. 실제로는 일종의 상위 오브젝트에서 파생되지 않는다. 이것은 모듈이 의미있는 곳입니다. 두 모델 모두 질문을 입력했기 때문에 수퍼 클래스가 적합하므로 기본 클래스 인 Question에서 파생되는 것이 적절합니다.

+0

단일 테이블, 오른쪽. 나는 너무 늦었다 고 생각한다. 로간 감사합니다! 그래서, 상속과 같은 소리가 좋은데 두 가지 가능성이 있습니다. 1) Question Abstract를 작성하고 각 서브 클래스에 자체 테이블을 제공하십시오. 또는 2) 비 추상적 인 질문을하고 STI를 사용하십시오 (정확하게). 나는 옵션 1을 좋아하지만 지금은 그저 가지고 노는 것에서부터 'survey.questions'를 더 이상 호출 할 수 없어서 모든 서브 클래 싱 된 질문을 얻지 못합니다 - 정확합니까? 나는 내 자신의 survey.questions 방법을 써야 하나? – Aeonaut

관련 문제