STI, 하나의 컨트롤러
저는 Rails를 처음 사용하고이 디자인 문제에 갇혀 있습니다. 쉽게 해결할 수 있지만 아무데도 얻지 못합니다. 하이라이트와 할인이라는 두 가지 유형의 광고가 있습니다. 둘 다 제목, 설명 및 하나의 이미지 (종이 클립 포함)와 같은 동일한 속성을 갖습니다. 또한 색인, 새로 작성, 편집, 작성, 업데이트 및 삭제와 같은 동일한 종류의 작업을 적용 할 수 있습니다.
다음과 같이 STI를 설정했습니다.
광고 모델 : ad.rb
class Ad < ActiveRecord::Base
end
바겐 세일 모델 : bargain.rb
class Bargain < Ad
end
하이라이트 모델 : highlight.rb
class Highlight < Ad
end
문제는 AdsController
www.foo.com/bargains [/ ...] 또는 www.foo.com과 같이 URL에 따라 할인 또는 하이라이트에 대해 말한 작업을 실행하는 컨트롤러 ( )를 하나만 갖고 싶다는 것입니다. /하이라이트[/...].
예를 들면 :
- GET www.foo.com/highlights => 하이라이트 인 모든 광고 목록.
- GET www.foo.com/highlights/new => 새로운 하이라이트 등을 만들기위한 양식 ...
어떻게 할 수 있습니까?
감사!
먼저. 새로운 경로 추가 :
resources :highlights, :controller => "ads", :type => "Highlight"
resources :bargains, :controller => "ads", :type => "Bargain"
그리고 AdsController
. 예를 들면 :
def new
@ad = Ad.new()
@ad.type = params[:type]
end
이 모든 컨트롤러 작업에 대한 최상의 접근 방식을 보려면 이 주석을 보십시오 .
그게 다야. 이제로 이동할 수 localhost:3000/highlights/new
있으며 new Highlight
가 초기화됩니다.
인덱스 작업은 다음과 같습니다.
def index
@ads = Ad.where(:type => params[:type])
end
로 이동하면 localhost:3000/highlights
하이라이트 목록이 나타납니다.
할인에 대한 동일한 방법 :localhost:3000/bargains
기타
URL
<%= link_to 'index', :highlights %>
<%= link_to 'new', [:new, :highlight] %>
<%= link_to 'edit', [:edit, @ad] %>
<%= link_to 'destroy', @ad, :method => :delete %>
다형성을 위해 :)
<%= link_to 'index', @ad.class %>
fl00r에는 좋은 해결책이 있지만 한 가지 조정을합니다.
이것은 귀하의 경우에 필요할 수도 있고 필요하지 않을 수도 있습니다. STI 모델, 특히 유효성 검사 및 수명주기 후크에서 변경되는 동작에 따라 다릅니다.
컨트롤러에 private 메서드를 추가하여 유형 매개 변수를 사용하려는 실제 클래스 상수로 변환합니다.
def ad_type
params[:type].constantize
end
그러나 위의 내용은 안전하지 않습니다. 유형의 허용 목록을 추가합니다.
def ad_types
[MyType, MyType2]
end
def ad_type
params[:type].constantize if params[:type].in? ad_types
end
레일 상수 화 방법에 대한 자세한 내용은 http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize를 참조 하십시오.
그런 다음 컨트롤러 작업에서 다음을 수행 할 수 있습니다.
def new
ad_type.new
end
def create
ad_type.new(params)
# ...
end
def index
ad_type.all
end
이제 속성 유형이 설정된 상위 클래스 대신 올바른 동작으로 실제 클래스를 사용하고 있습니다.
이 주제와 관련된 흥미로운 트릭이 많이 있기 때문에이 링크를 포함하고 싶었습니다.
나는 이것이 @flOOr 및 @Alan_Peabody의 답변을 포함하는 내가 좋아하는 패턴이라는 오래된 질문이라는 것을 알고 있습니다. (Rails 4.2에서 테스트되었으며 아마도 Rails 5에서 작동 할 것입니다)
모델에서 시작시 화이트리스트를 만듭니다. dev에서 이것은 열심히로드되어야합니다.
class Ad < ActiveRecord::Base
Rails.application.eager_load! if Rails.env.development?
TYPE_NAMES = self.subclasses.map(&:name)
#You can add validation like the answer by @dankohn
end
이제 모든 컨트롤러에서이 화이트리스트를 참조하여 올바른 범위를 빌드 할 수있을뿐만 아니라 양식에서 : type 선택을위한 컬렉션 등을 만들 수 있습니다.
class AdsController < ApplicationController
before_action :set_ad, :only => [:show, :compare, :edit, :update, :destroy]
def new
@ad = ad_scope.new
end
def create
@ad = ad_scope.new(ad_params)
#the usual stuff comes next...
end
private
def set_ad
#works as normal but we use our scope to ensure subclass
@ad = ad_scope.find(params[:id])
end
#return the scope of a Ad STI subclass based on params[:type] or default to Ad
def ad_scope
#This could also be done in some kind of syntax that makes it more like a const.
@ad_scope ||= params[:type].try(:in?, Ad::TYPE_NAMES) ? params[:type].constantize : Ad
end
#strong params check works as expected
def ad_params
params.require(:ad).permit({:foo})
end
end
객체의 실제 : type에도 불구하고 라우팅이 기본 클래스 컨트롤러로 전송되어야하므로 양식을 처리해야합니다. 이를 위해 "becomes"를 사용하여 양식 작성기를 올바른 라우팅으로 속이고 : as 지시문을 사용하여 입력 이름도 기본 클래스가되도록합니다. 이 조합을 사용하면 수정되지 않은 경로 (리소스 : ads)를 사용할 수있을뿐만 아니라 양식에서 돌아 오는 params [: ad]에 대한 강력한 매개 변수 검사도 사용할 수 있습니다.
#/views/ads/_form.html.erb
<%= form_for(@ad.becomes(Ad), :as => :ad) do |f| %>
[완벽하게 작동하는 더 간단한 솔루션으로 다시 작성 :]
Iterating on the other answers, I have come up with the following solution for a single controller with Single Table Inheritance that works well with Strong Parameters in Rails 4.1. Just including :type as a permitted parameter caused an ActiveRecord::SubclassNotFound
error if an invalid type is entered. Moreover, type is not updated because the SQL query explicitly looks for the old type. Instead, :type
needs to be updated separately with update_column if it is different than what is current set and is a valid type. Note also that I've succeeded in DRYing up all lists of types.
# app/models/company.rb
class Company < ActiveRecord::Base
COMPANY_TYPES = %w[Publisher Buyer Printer Agent]
validates :type, inclusion: { in: COMPANY_TYPES,
:message => "must be one of: #{COMPANY_TYPES.join(', ')}" }
end
Company::COMPANY_TYPES.each do |company_type|
string_to_eval = <<-heredoc
class #{company_type} < Company
def self.model_name # http://stackoverflow.com/a/12762230/1935918
Company.model_name
end
end
heredoc
eval(string_to_eval, TOPLEVEL_BINDING)
end
And in the controller:
# app/controllers/companies_controller.rb
def update
@company = Company.find(params[:id])
# This separate step is required to change Single Table Inheritance types
new_type = params[:company][:type]
if new_type != @company.type && Company::COMPANY_TYPES.include?(new_type)
@company.update_column :type, new_type
end
@company.update(company_params)
respond_with(@company)
end
And routes:
# config/routes.rb
Rails.application.routes.draw do
resources :companies
Company::COMPANY_TYPES.each do |company_type|
resources company_type.underscore.to_sym, type: company_type, controller: 'companies', path: 'companies'
end
root 'companies#index'
Finally, I recommend using the responders gem and setting scaffolding to use a responders_controller, which is compatible with STI. Config for scaffolding is:
# config/application.rb
config.generators do |g|
g.scaffold_controller "responders_controller"
end
참고URL : https://stackoverflow.com/questions/5246767/sti-one-controller
'Programing' 카테고리의 다른 글
테이블에서 모두 삭제 (0) | 2020.12.10 |
---|---|
HTML5 캔버스에 회전 된 텍스트 그리기 (0) | 2020.12.10 |
iOS에서 기기 위치 (국가 만 해당) 가져 오기 (0) | 2020.12.10 |
Ubuntu에서 영구적으로 PATH 변경 (0) | 2020.12.10 |
/ test 클래스에 대한 구성 요소를 생성하지 않는 Dagger (0) | 2020.12.10 |